Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
# API load / smoke harness
|
||||
|
||||
Minimal stdlib Go harness for three critical read paths. Prefer this when **k6 / hey / vegeta** are not installed (none are required or vendored in this repo).
|
||||
|
||||
## Endpoints
|
||||
|
||||
| Name | Method | Path | Auth |
|
||||
|------|--------|------|------|
|
||||
| healthz | GET | `/healthz` | none |
|
||||
| feeds | GET | `/api/v1/feeds?limit=10&offset=0` | Bearer API key |
|
||||
| products | GET | `/api/v1/products?limit=10&offset=0` | Bearer API key |
|
||||
|
||||
Default base URL matches `HTTP_ADDR` (`:28471` → `http://127.0.0.1:28471`). Demo key: see `docs/demo-user.md` (`dk_demo_local_descrybe_test_key_v1`).
|
||||
|
||||
`RateLimitV1Process` applies only to heavy **POST** mutations (process / feed sync / export generate), not these GETs. DB and laptop CPU are still the real limits.
|
||||
|
||||
## RPS assumptions
|
||||
|
||||
| Profile | Flags | Intent | Aggregate RPS |
|
||||
|---------|-------|--------|---------------|
|
||||
| **smoke** (default) | `-c 2 -d 5s -rate 6` | Availability + latency sample; safe on local Postgres | **~6 RPS** capped (not “as fast as possible”) |
|
||||
| **load** (manual) | e.g. `-mode load -c 8 -d 30s -rate 40` | Staging / dedicated API only | Tens of RPS; watch p95 and Postgres |
|
||||
| **unlimited** | `-mode load -rate 0` | Intentional soak only | Uncapped — **do not** use against shared laptop DB |
|
||||
| **capacity** | external tool | Real soak / SLO | k6/hey/vegeta on staging |
|
||||
|
||||
ASSUMPTION: smoke is a correctness/latency probe, not a capacity claim. Concurrency alone does not cap RPS — **`-rate` does**. Smoke refuses `-rate 0`.
|
||||
|
||||
## Smoke (local)
|
||||
|
||||
API must already be running (`scripts/run-api.ps1` or equivalent).
|
||||
|
||||
```powershell
|
||||
cd f:\laragon\www\_MY\descrybe-v2\scripts\api-load-smoke
|
||||
go run .
|
||||
```
|
||||
|
||||
Overrides:
|
||||
|
||||
```powershell
|
||||
$env:API_BASE = "http://127.0.0.1:28471"
|
||||
$env:API_KEY = "dk_demo_local_descrybe_test_key_v1"
|
||||
cd f:\laragon\www\_MY\descrybe-v2\scripts\api-load-smoke
|
||||
go run . -c 2 -d 5s -rate 6 -mode smoke
|
||||
```
|
||||
|
||||
Exit code `1` if any non-cancelled request fails (`-fail-on-error=false` to only print stats).
|
||||
|
||||
## Larger tests
|
||||
|
||||
Still this harness (gentle step-up):
|
||||
|
||||
```powershell
|
||||
cd f:\laragon\www\_MY\descrybe-v2\scripts\api-load-smoke
|
||||
go run . -mode load -c 8 -d 30s -rate 40 -base http://127.0.0.1:28471
|
||||
```
|
||||
|
||||
Or external tools (install separately; point at **staging**):
|
||||
|
||||
```bash
|
||||
# hey — fixed request count
|
||||
hey -n 200 -c 8 -H "Authorization: Bearer $API_KEY" "$BASE/api/v1/products?limit=10"
|
||||
|
||||
# vegeta — rate explicitly
|
||||
echo "GET $BASE/healthz" | vegeta attack -duration=30s -rate=20 | vegeta report
|
||||
|
||||
# k6 — script your own scenarios; keep rate modest on shared DBs
|
||||
```
|
||||
|
||||
Related: `docs/api-surface-smoke.md` (functional matrix), `docs/perf-notes.md` (list-path notes), `scripts/v1-process-smoke` (legacy items create/process + poll).
|
||||
@@ -0,0 +1,3 @@
|
||||
module github.com/descrybe/descrybe-v2/scripts/api-load-smoke
|
||||
|
||||
go 1.22
|
||||
@@ -0,0 +1,275 @@
|
||||
// Command api-load-smoke: short, low-concurrency HTTP smoke against critical API paths.
|
||||
//
|
||||
// Default profile is intentionally gentle (local laptop / shared Postgres).
|
||||
// It is NOT a capacity test — see README.md for larger profiles and RPS assumptions.
|
||||
//
|
||||
// Usage (API listening — default HTTP_ADDR :28471):
|
||||
//
|
||||
// cd scripts/api-load-smoke && go run .
|
||||
// go run . -base http://127.0.0.1:28471 -c 2 -d 5s -rate 6
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const demoAPIKey = "dk_demo_local_descrybe_test_key_v1"
|
||||
|
||||
// Smoke defaults — capped aggregate RPS so local Postgres is not hammered.
|
||||
const (
|
||||
defaultConcurrency = 2
|
||||
defaultDuration = 5 * time.Second
|
||||
defaultTimeout = 10 * time.Second
|
||||
defaultRateRPS = 6.0 // aggregate across all workers/endpoints
|
||||
defaultLimitQuery = "limit=10&offset=0"
|
||||
)
|
||||
|
||||
type endpoint struct {
|
||||
name string
|
||||
path string
|
||||
auth bool
|
||||
}
|
||||
|
||||
type stats struct {
|
||||
ok atomic.Int64
|
||||
fail atomic.Int64
|
||||
latMu sync.Mutex
|
||||
latNanos []int64
|
||||
}
|
||||
|
||||
func (s *stats) record(ok bool, d time.Duration) {
|
||||
if ok {
|
||||
s.ok.Add(1)
|
||||
} else {
|
||||
s.fail.Add(1)
|
||||
}
|
||||
s.latMu.Lock()
|
||||
s.latNanos = append(s.latNanos, d.Nanoseconds())
|
||||
s.latMu.Unlock()
|
||||
}
|
||||
|
||||
func (s *stats) snapshot() (ok, fail int64, min, avg, p95, max time.Duration) {
|
||||
ok, fail = s.ok.Load(), s.fail.Load()
|
||||
s.latMu.Lock()
|
||||
defer s.latMu.Unlock()
|
||||
n := len(s.latNanos)
|
||||
if n == 0 {
|
||||
return ok, fail, 0, 0, 0, 0
|
||||
}
|
||||
cp := append([]int64(nil), s.latNanos...)
|
||||
sort.Slice(cp, func(i, j int) bool { return cp[i] < cp[j] })
|
||||
var sum int64
|
||||
for _, v := range cp {
|
||||
sum += v
|
||||
}
|
||||
min = time.Duration(cp[0])
|
||||
max = time.Duration(cp[n-1])
|
||||
avg = time.Duration(sum / int64(n))
|
||||
idx := (n * 95) / 100
|
||||
if idx >= n {
|
||||
idx = n - 1
|
||||
}
|
||||
p95 = time.Duration(cp[idx])
|
||||
return ok, fail, min, avg, p95, max
|
||||
}
|
||||
|
||||
func main() {
|
||||
base := flag.String("base", envOr("API_BASE", "http://127.0.0.1:28471"), "API base URL (no trailing slash)")
|
||||
key := flag.String("key", envOr("API_KEY", demoAPIKey), "API key for /api/v1 feeds+products (Bearer)")
|
||||
c := flag.Int("c", defaultConcurrency, "worker concurrency (smoke: 2; load: raise carefully)")
|
||||
d := flag.Duration("d", defaultDuration, "test duration (smoke: 5s)")
|
||||
rate := flag.Float64("rate", defaultRateRPS, "max aggregate requests/sec (0 = unlimited; smoke default 6)")
|
||||
timeout := flag.Duration("timeout", defaultTimeout, "per-request timeout")
|
||||
mode := flag.String("mode", "smoke", "smoke|load (banner + safety hint; still honor -c/-d/-rate)")
|
||||
failOnErr := flag.Bool("fail-on-error", true, "exit 1 if any request fails")
|
||||
flag.Parse()
|
||||
|
||||
if *c < 1 {
|
||||
fmt.Fprintln(os.Stderr, "-c must be >= 1")
|
||||
os.Exit(2)
|
||||
}
|
||||
if *d <= 0 {
|
||||
fmt.Fprintln(os.Stderr, "-d must be > 0")
|
||||
os.Exit(2)
|
||||
}
|
||||
if *rate < 0 {
|
||||
fmt.Fprintln(os.Stderr, "-rate must be >= 0")
|
||||
os.Exit(2)
|
||||
}
|
||||
if strings.EqualFold(*mode, "smoke") && *rate == 0 {
|
||||
fmt.Fprintln(os.Stderr, "refusing -rate 0 in smoke mode (would DDOS local stack); use -mode load -rate 0 intentionally")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
baseURL := strings.TrimRight(strings.TrimSpace(*base), "/")
|
||||
endpoints := []endpoint{
|
||||
{name: "healthz", path: "/healthz", auth: false},
|
||||
{name: "feeds", path: "/api/v1/feeds?" + defaultLimitQuery, auth: true},
|
||||
{name: "products", path: "/api/v1/products?" + defaultLimitQuery, auth: true},
|
||||
}
|
||||
|
||||
client := &http.Client{Timeout: *timeout}
|
||||
st := make([]*stats, len(endpoints))
|
||||
for i := range st {
|
||||
st[i] = &stats{}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), *d)
|
||||
defer cancel()
|
||||
|
||||
tokens := startRateGate(ctx, *rate)
|
||||
|
||||
fmt.Printf("api-load-smoke mode=%s base=%s c=%d d=%s rate=%.1f\n", *mode, baseURL, *c, d.String(), *rate)
|
||||
fmt.Println("ASSUMPTION: local smoke defaults to ~6 aggregate RPS (c=2, 5s); raise -rate/-c only on dedicated/staging.")
|
||||
if strings.EqualFold(*mode, "load") {
|
||||
fmt.Println("NOTE: prefer staging. GET lists are not under RateLimitV1Process (mutations only); DB is still the bottleneck.")
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
start := time.Now()
|
||||
for w := 0; w < *c; w++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
i := 0
|
||||
for {
|
||||
if !waitToken(ctx, tokens) {
|
||||
return
|
||||
}
|
||||
ep := endpoints[i%len(endpoints)]
|
||||
si := st[i%len(endpoints)]
|
||||
i++
|
||||
ok, lat, cancelled := doRequest(ctx, client, baseURL, *key, ep)
|
||||
if cancelled {
|
||||
return
|
||||
}
|
||||
si.record(ok, lat)
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
elapsed := time.Since(start)
|
||||
|
||||
var totalOK, totalFail int64
|
||||
fmt.Println()
|
||||
fmt.Printf("%-10s %8s %8s %10s %10s %10s %10s\n", "endpoint", "ok", "fail", "min", "avg", "p95", "max")
|
||||
for i, ep := range endpoints {
|
||||
ok, fail, min, avg, p95, max := st[i].snapshot()
|
||||
totalOK += ok
|
||||
totalFail += fail
|
||||
fmt.Printf("%-10s %8d %8d %10s %10s %10s %10s\n",
|
||||
ep.name, ok, fail, fmtDur(min), fmtDur(avg), fmtDur(p95), fmtDur(max))
|
||||
}
|
||||
total := totalOK + totalFail
|
||||
rps := 0.0
|
||||
if elapsed > 0 {
|
||||
rps = float64(total) / elapsed.Seconds()
|
||||
}
|
||||
fmt.Printf("\ntotal_ok=%d total_fail=%d elapsed=%s approx_rps=%.1f\n", totalOK, totalFail, elapsed.Round(time.Millisecond), rps)
|
||||
|
||||
if totalFail > 0 && *failOnErr {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// startRateGate returns nil for unlimited; otherwise a token channel paced at rate RPS.
|
||||
func startRateGate(ctx context.Context, rate float64) <-chan struct{} {
|
||||
if rate <= 0 {
|
||||
return nil
|
||||
}
|
||||
ch := make(chan struct{})
|
||||
interval := time.Duration(float64(time.Second) / rate)
|
||||
if interval < time.Millisecond {
|
||||
interval = time.Millisecond
|
||||
}
|
||||
go func() {
|
||||
t := time.NewTicker(interval)
|
||||
defer t.Stop()
|
||||
// First token immediately so smoke does not wait a full interval.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case ch <- struct{}{}:
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-t.C:
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case ch <- struct{}{}:
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func waitToken(ctx context.Context, tokens <-chan struct{}) bool {
|
||||
if tokens == nil {
|
||||
return ctx.Err() == nil
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return false
|
||||
case <-tokens:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func doRequest(ctx context.Context, client *http.Client, base, key string, ep endpoint) (ok bool, lat time.Duration, cancelled bool) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, base+ep.path, nil)
|
||||
if err != nil {
|
||||
return false, 0, ctx.Err() != nil
|
||||
}
|
||||
req.Header.Set("Accept", "application/json")
|
||||
if ep.auth {
|
||||
req.Header.Set("Authorization", "Bearer "+key)
|
||||
}
|
||||
t0 := time.Now()
|
||||
res, err := client.Do(req)
|
||||
lat = time.Since(t0)
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
return false, lat, true
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "%s error: %v\n", ep.name, err)
|
||||
return false, lat, false
|
||||
}
|
||||
defer res.Body.Close()
|
||||
_, _ = io.Copy(io.Discard, io.LimitReader(res.Body, 1<<20))
|
||||
if res.StatusCode < 200 || res.StatusCode >= 300 {
|
||||
fmt.Fprintf(os.Stderr, "%s status=%d\n", ep.name, res.StatusCode)
|
||||
return false, lat, false
|
||||
}
|
||||
return true, lat, false
|
||||
}
|
||||
|
||||
func envOr(key, fallback string) string {
|
||||
if v := strings.TrimSpace(os.Getenv(key)); v != "" {
|
||||
return v
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
func fmtDur(d time.Duration) string {
|
||||
if d <= 0 {
|
||||
return "-"
|
||||
}
|
||||
if d < time.Millisecond {
|
||||
return d.Round(time.Microsecond).String()
|
||||
}
|
||||
return d.Round(time.Millisecond).String()
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
-- One-shot / re-runnable local cleanup for descrybe-v2.
|
||||
-- Keeps: Platform Demo, A1 Slovenija, Guide Shopify, Guide WooCommerce
|
||||
-- (matched case-insensitively by name).
|
||||
-- Keeps plans: Free/Starter/Growth/Business/Enterprise + A1 + Platform Demo
|
||||
-- + Legacy + archived Basic/Professional + Merkur trial (code/catalog).
|
||||
-- Deletes orphan users that only belonged to removed companies.
|
||||
-- Usage:
|
||||
-- psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f scripts/cleanup-dev-tenants.sql
|
||||
-- Never prints connection secrets.
|
||||
|
||||
BEGIN;
|
||||
|
||||
CREATE TEMP TABLE keep_companies ON COMMIT DROP AS
|
||||
SELECT id, name
|
||||
FROM companies
|
||||
WHERE
|
||||
lower(name) = 'platform demo'
|
||||
OR lower(name) = 'a1 slovenija'
|
||||
OR lower(name) LIKE '%shopify%'
|
||||
OR lower(name) LIKE '%woocommerce%'
|
||||
OR lower(name) ~ '(^|[^a-z0-9])woo([^a-z0-9]|$)'
|
||||
OR (
|
||||
lower(name) ~ '(^|[^a-z0-9])a1([^a-z0-9]|$)'
|
||||
AND lower(name) NOT LIKE 'vars-page%'
|
||||
)
|
||||
OR (
|
||||
lower(name) LIKE '%demo%'
|
||||
AND lower(name) NOT LIKE '%auth smoke%'
|
||||
);
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
n int;
|
||||
BEGIN
|
||||
SELECT COUNT(*) INTO n FROM keep_companies;
|
||||
IF n < 4 THEN
|
||||
RAISE EXCEPTION 'keep_companies too small (%) - aborting cleanup', n;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
CREATE TEMP TABLE delete_companies ON COMMIT DROP AS
|
||||
SELECT c.id, c.name
|
||||
FROM companies c
|
||||
WHERE c.id NOT IN (SELECT id FROM keep_companies);
|
||||
|
||||
CREATE TEMP TABLE cleanup_stats (
|
||||
key text PRIMARY KEY,
|
||||
value bigint NOT NULL
|
||||
) ON COMMIT DROP;
|
||||
|
||||
INSERT INTO cleanup_stats(key, value) VALUES
|
||||
('companies_kept', (SELECT COUNT(*) FROM keep_companies)),
|
||||
('companies_deleted', (SELECT COUNT(*) FROM delete_companies)),
|
||||
('plans_before', (SELECT COUNT(*) FROM plans)),
|
||||
('users_before', (SELECT COUNT(*) FROM users)),
|
||||
('sessions_before', (SELECT COUNT(*) FROM sessions));
|
||||
|
||||
-- Opaque session store (token/data/expiry) — wipe so deleted-user cookies die.
|
||||
DELETE FROM sessions;
|
||||
|
||||
DELETE FROM companies
|
||||
WHERE id IN (SELECT id FROM delete_companies);
|
||||
|
||||
CREATE TEMP TABLE keep_users ON COMMIT DROP AS
|
||||
SELECT u.id, u.email
|
||||
FROM users u
|
||||
WHERE
|
||||
lower(u.email) IN (
|
||||
'demo@descrybe.local',
|
||||
'demo@descrybe.test',
|
||||
'a1-primary@descrybe.local',
|
||||
'guide-shopify@descrybe.local',
|
||||
'guide-woocommerce@descrybe.local'
|
||||
)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM memberships m WHERE m.user_id = u.id
|
||||
);
|
||||
|
||||
DELETE FROM users
|
||||
WHERE id NOT IN (SELECT id FROM keep_users);
|
||||
|
||||
CREATE TEMP TABLE keep_plans ON COMMIT DROP AS
|
||||
SELECT p.id, p.name
|
||||
FROM plans p
|
||||
WHERE
|
||||
lower(p.name) IN (
|
||||
'free', 'starter', 'growth', 'business', 'enterprise',
|
||||
'a1', 'platform demo', 'legacy',
|
||||
'basic', 'professional', 'merkur trial'
|
||||
)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM company_plans cp WHERE cp.plan_id = p.id
|
||||
);
|
||||
|
||||
DELETE FROM plans
|
||||
WHERE id NOT IN (SELECT id FROM keep_plans);
|
||||
|
||||
INSERT INTO cleanup_stats(key, value) VALUES
|
||||
('plans_after', (SELECT COUNT(*) FROM plans)),
|
||||
('companies_after', (SELECT COUNT(*) FROM companies)),
|
||||
('users_after', (SELECT COUNT(*) FROM users)),
|
||||
('sessions_after', (SELECT COUNT(*) FROM sessions));
|
||||
|
||||
SELECT key, value FROM cleanup_stats ORDER BY key;
|
||||
SELECT id, name AS kept_company FROM keep_companies ORDER BY lower(name);
|
||||
SELECT id, name AS kept_plan FROM plans ORDER BY id;
|
||||
SELECT id, email AS kept_user FROM users ORDER BY email;
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,80 @@
|
||||
-- Hard-delete leftover v1-process-smoke EANs (8700999...) from Platform Demo only.
|
||||
--
|
||||
-- There is no product soft-delete HTTP API (products are GET/PATCH only).
|
||||
-- This script is the safe admin/ops path for Demo cleanup.
|
||||
--
|
||||
-- NEVER touches A1 Slovenija (or A1 cohort aliases). Refuses if the resolved
|
||||
-- target is missing, ambiguous, or looks like A1.
|
||||
--
|
||||
-- Usage (PowerShell):
|
||||
-- psql $env:DATABASE_URL -v ON_ERROR_STOP=1 -f scripts/cleanup-process-smoke-eans.sql
|
||||
--
|
||||
-- Preview only (no deletes):
|
||||
-- SELECT c.name, r.gtin FROM raw_products r
|
||||
-- JOIN companies c ON c.id = r.company_id
|
||||
-- WHERE lower(c.name) = 'platform demo' AND r.gtin LIKE '8700999%';
|
||||
--
|
||||
-- Go path: cd apps/api && go run ./cmd/seed-demo -purge-smoke-eans
|
||||
-- Related: scripts/v1-process-smoke, docs/safe-test-fixtures.md
|
||||
|
||||
BEGIN;
|
||||
|
||||
CREATE TEMP TABLE smoke_demo_target ON COMMIT DROP AS
|
||||
SELECT c.id,
|
||||
c.name,
|
||||
COALESCE(c.legacy_company_id, '') AS legacy_company_id
|
||||
FROM companies c
|
||||
WHERE lower(c.name) = 'platform demo';
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
n int;
|
||||
leg text;
|
||||
nm text;
|
||||
BEGIN
|
||||
SELECT COUNT(*) INTO n FROM smoke_demo_target;
|
||||
IF n <> 1 THEN
|
||||
RAISE EXCEPTION 'Platform Demo company not found uniquely (count=%). Aborting.', n;
|
||||
END IF;
|
||||
|
||||
SELECT legacy_company_id, name INTO leg, nm FROM smoke_demo_target;
|
||||
|
||||
IF lower(leg) = '97e1a309-3d23-4aa2-b518-8e8d7afdfec7' THEN
|
||||
RAISE EXCEPTION 'refusing smoke EAN cleanup: target has A1 legacy_company_id';
|
||||
END IF;
|
||||
IF lower(nm) IN ('a1 slovenija', 'a1', 'local demo co') THEN
|
||||
RAISE EXCEPTION 'refusing smoke EAN cleanup: target name % is an A1 cohort alias', nm;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM raw_products r
|
||||
JOIN smoke_demo_target t ON t.id = r.company_id
|
||||
WHERE r.gtin LIKE '8700999%') AS raw_count,
|
||||
(SELECT COUNT(*) FROM processed_products p
|
||||
JOIN smoke_demo_target t ON t.id = p.company_id
|
||||
WHERE p.product_id LIKE '8700999%') AS processed_count;
|
||||
|
||||
DELETE FROM processing_job_products pjp
|
||||
WHERE pjp.raw_product_id IN (
|
||||
SELECT r.id FROM raw_products r
|
||||
JOIN smoke_demo_target t ON t.id = r.company_id
|
||||
WHERE r.gtin LIKE '8700999%'
|
||||
)
|
||||
OR pjp.processed_product_id IN (
|
||||
SELECT p.id FROM processed_products p
|
||||
JOIN smoke_demo_target t ON t.id = p.company_id
|
||||
WHERE p.product_id LIKE '8700999%'
|
||||
);
|
||||
|
||||
DELETE FROM processed_products p
|
||||
USING smoke_demo_target t
|
||||
WHERE p.company_id = t.id
|
||||
AND p.product_id LIKE '8700999%';
|
||||
|
||||
DELETE FROM raw_products r
|
||||
USING smoke_demo_target t
|
||||
WHERE r.company_id = t.id
|
||||
AND r.gtin LIKE '8700999%';
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,284 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Read-only cutover deploy gates (no migrate/up, no restarts, no infra changes).
|
||||
*
|
||||
* Checks:
|
||||
* 1) goose status includes applied 039_worker_heartbeats + 040_job_hotpath_indexes + 041_password_reset_tokens + 042_user_session_version
|
||||
* 2) GET /readyz reports checks.worker=ok (fresh processing heartbeat <=60s)
|
||||
* 3) adapter-node host gates (pin, svelte.config import, build/start scripts)
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/cutover-deploy-check.mjs
|
||||
* npm run cutover:deploy-check
|
||||
* npm run cutover:deploy-check:code
|
||||
* node scripts/cutover-deploy-check.mjs --skip-goose
|
||||
* node scripts/cutover-deploy-check.mjs --skip-readyz
|
||||
* HEALTH_BASE_URL=https://api.example.com node scripts/cutover-deploy-check.mjs
|
||||
*/
|
||||
import { spawnSync } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { DEV_API_PORT, DEV_PUBLIC_API_URL } from "./dev-ports.mjs";
|
||||
import { loadRootEnv, repoRoot } from "./root-env.mjs";
|
||||
|
||||
const REQUIRED_MIGRATIONS = [
|
||||
"039_worker_heartbeats",
|
||||
"040_job_hotpath_indexes",
|
||||
"041_password_reset_tokens",
|
||||
"042_user_session_version",
|
||||
];
|
||||
const ADAPTER_NODE_PIN = "5.5.7";
|
||||
|
||||
const args = new Set(process.argv.slice(2));
|
||||
const skipGoose = args.has("--skip-goose");
|
||||
const skipReadyz = args.has("--skip-readyz");
|
||||
const wantHelp = args.has("-h") || args.has("--help");
|
||||
|
||||
if (wantHelp) {
|
||||
console.log(`Usage: node scripts/cutover-deploy-check.mjs [--skip-goose] [--skip-readyz]
|
||||
|
||||
Read-only cutover gates for goose 039+040+041+042, worker /readyz heartbeat, and
|
||||
adapter-node host gates (pin + svelte.config + build/start scripts). Does not
|
||||
run goose up, restart processes, or change infra.
|
||||
|
||||
Env: DATABASE_URL (goose), PUBLIC_API_URL / HEALTH_BASE_URL (readyz).
|
||||
Alias: npm run cutover:deploy-check
|
||||
npm run cutover:deploy-check:code (--skip-goose --skip-readyz)`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const env = loadRootEnv(process.env);
|
||||
const apiDir = path.join(repoRoot, "apps", "api");
|
||||
const failures = [];
|
||||
|
||||
function section(title) {
|
||||
console.log(`\n==> ${title}`);
|
||||
}
|
||||
|
||||
function pass(msg) {
|
||||
console.log(`PASS ${msg}`);
|
||||
}
|
||||
|
||||
function fail(msg) {
|
||||
console.log(`FAIL ${msg}`);
|
||||
failures.push(msg);
|
||||
}
|
||||
|
||||
function info(msg) {
|
||||
console.log(`NOTE ${msg}`);
|
||||
}
|
||||
|
||||
function parseReadyz(text) {
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function migrationApplied(statusText, id) {
|
||||
const lineMatch = statusText
|
||||
.split(/\r?\n/)
|
||||
.map((l) => l.trim())
|
||||
.filter((l) => l.includes(id));
|
||||
if (lineMatch.length === 0) return { found: false, pending: false };
|
||||
const pending = lineMatch.every((l) => /\bPending\b/i.test(l));
|
||||
if (pending) return { found: true, pending: true };
|
||||
return { found: true, pending: false };
|
||||
}
|
||||
|
||||
function checkGoose() {
|
||||
section("goose status (039 + 040 + 041 + 042)");
|
||||
if (skipGoose) {
|
||||
info("skipped (--skip-goose)");
|
||||
return;
|
||||
}
|
||||
if (!env.DATABASE_URL) {
|
||||
fail("DATABASE_URL unset - cannot verify goose 039/040/041/042 (set root .env or shell)");
|
||||
return;
|
||||
}
|
||||
const r = spawnSync(
|
||||
"go",
|
||||
[
|
||||
"run",
|
||||
"github.com/pressly/goose/v3/cmd/goose@v3.24.3",
|
||||
"-dir",
|
||||
"sql/schema",
|
||||
"postgres",
|
||||
env.DATABASE_URL,
|
||||
"status",
|
||||
],
|
||||
{
|
||||
cwd: apiDir,
|
||||
env,
|
||||
encoding: "utf8",
|
||||
shell: process.platform === "win32",
|
||||
},
|
||||
);
|
||||
if (r.error) {
|
||||
fail(`goose status spawn failed: ${r.error.message}`);
|
||||
return;
|
||||
}
|
||||
const out = `${r.stdout || ""}${r.stderr || ""}`;
|
||||
if (r.status !== 0) {
|
||||
fail(`goose status exited ${r.status}`);
|
||||
if (out.trim()) console.log(out.trim());
|
||||
return;
|
||||
}
|
||||
console.log(out.trim() || "(empty goose status)");
|
||||
for (const id of REQUIRED_MIGRATIONS) {
|
||||
const st = migrationApplied(out, id);
|
||||
if (!st.found) {
|
||||
fail(`migration ${id} not listed in goose status`);
|
||||
} else if (st.pending) {
|
||||
fail(`migration ${id} is Pending - run npm run migrate (ops window), then re-check`);
|
||||
} else {
|
||||
pass(`${id} applied`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function checkReadyz() {
|
||||
section("/readyz worker heartbeat");
|
||||
if (skipReadyz) {
|
||||
info("skipped (--skip-readyz)");
|
||||
return;
|
||||
}
|
||||
const base = (
|
||||
env.HEALTH_BASE_URL ||
|
||||
env.PUBLIC_API_URL ||
|
||||
DEV_PUBLIC_API_URL ||
|
||||
`http://127.0.0.1:${DEV_API_PORT}`
|
||||
).replace(/\/$/, "");
|
||||
const url = `${base}/readyz`;
|
||||
let res;
|
||||
let text;
|
||||
try {
|
||||
res = await fetch(url);
|
||||
text = await res.text();
|
||||
} catch (err) {
|
||||
fail(
|
||||
`GET ${url} failed: ${err instanceof Error ? err.message : err} (start api+worker; npm run dev)`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
console.log(`GET ${url} -> ${res.status}`);
|
||||
console.log(text);
|
||||
const body = parseReadyz(text);
|
||||
const worker = body?.checks?.worker;
|
||||
const age = body?.worker_last_seen_age_s;
|
||||
if (res.status === 200 && worker === "ok") {
|
||||
pass(
|
||||
`worker heartbeat ok` +
|
||||
(typeof age === "number" ? ` (age_s=${age}, stale after 60s)` : ""),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (res.status === 503 && (worker === "missing" || worker === "stale")) {
|
||||
fail(
|
||||
`checks.worker=${worker} - restart cmd/worker after goose 039+040+041+042 (API-only -> 503 is expected)`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
fail(
|
||||
`/readyz not ready (status=${res.status}, checks.worker=${worker ?? "n/a"})`,
|
||||
);
|
||||
}
|
||||
|
||||
function checkAdapterNodeNotes() {
|
||||
section("adapter-node host notes (ops)");
|
||||
const pkgPath = path.join(repoRoot, "apps", "web", "package.json");
|
||||
const svelteCfgPath = path.join(repoRoot, "apps", "web", "svelte.config.js");
|
||||
const rootPkgPath = path.join(repoRoot, "package.json");
|
||||
let pkg = null;
|
||||
let rootPkg = null;
|
||||
try {
|
||||
pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
||||
} catch (err) {
|
||||
fail(`could not read apps/web/package.json: ${err instanceof Error ? err.message : err}`);
|
||||
return;
|
||||
}
|
||||
const pin =
|
||||
pkg?.dependencies?.["@sveltejs/adapter-node"] ??
|
||||
pkg?.devDependencies?.["@sveltejs/adapter-node"] ??
|
||||
null;
|
||||
if (pin === ADAPTER_NODE_PIN) {
|
||||
pass(`@sveltejs/adapter-node@${pin} pinned in apps/web/package.json`);
|
||||
} else {
|
||||
fail(
|
||||
`@sveltejs/adapter-node pin is ${JSON.stringify(pin)} (expected ${ADAPTER_NODE_PIN})`,
|
||||
);
|
||||
}
|
||||
try {
|
||||
const svelteCfg = fs.readFileSync(svelteCfgPath, "utf8");
|
||||
if (/from\s+["']@sveltejs\/adapter-node["']/.test(svelteCfg)) {
|
||||
pass("apps/web/svelte.config.js imports @sveltejs/adapter-node");
|
||||
} else {
|
||||
fail("apps/web/svelte.config.js does not import @sveltejs/adapter-node");
|
||||
}
|
||||
} catch (err) {
|
||||
fail(`could not read apps/web/svelte.config.js: ${err instanceof Error ? err.message : err}`);
|
||||
}
|
||||
const webBuild = pkg?.scripts?.build;
|
||||
const webStart = pkg?.scripts?.start;
|
||||
if (typeof webBuild === "string" && webBuild.trim()) {
|
||||
pass(`apps/web build script: ${webBuild}`);
|
||||
} else {
|
||||
fail("apps/web package.json missing scripts.build");
|
||||
}
|
||||
if (webStart === "node build") {
|
||||
pass("apps/web start script: node build (adapter-node output)");
|
||||
} else {
|
||||
fail(
|
||||
`apps/web scripts.start is ${JSON.stringify(webStart)} (expected "node build")`,
|
||||
);
|
||||
}
|
||||
try {
|
||||
rootPkg = JSON.parse(fs.readFileSync(rootPkgPath, "utf8"));
|
||||
} catch (err) {
|
||||
fail(`could not read root package.json: ${err instanceof Error ? err.message : err}`);
|
||||
}
|
||||
if (rootPkg) {
|
||||
if (rootPkg?.scripts?.["start:web"] === "npm run start --workspace=web") {
|
||||
pass("root start:web -> npm run start --workspace=web");
|
||||
} else {
|
||||
fail(
|
||||
`root scripts.start:web is ${JSON.stringify(rootPkg?.scripts?.["start:web"])} (expected "npm run start --workspace=web")`,
|
||||
);
|
||||
}
|
||||
if (rootPkg?.scripts?.["cutover:deploy-check"] === "node scripts/cutover-deploy-check.mjs") {
|
||||
pass("root cutover:deploy-check script present");
|
||||
} else {
|
||||
fail("root package.json missing scripts[\"cutover:deploy-check\"]");
|
||||
}
|
||||
if (
|
||||
rootPkg?.scripts?.["cutover:deploy-check:code"] ===
|
||||
"node scripts/cutover-deploy-check.mjs --skip-goose --skip-readyz"
|
||||
) {
|
||||
pass("root cutover:deploy-check:code script present");
|
||||
} else {
|
||||
fail("root package.json missing scripts[\"cutover:deploy-check:code\"]");
|
||||
}
|
||||
}
|
||||
info("Host still required: npm run build --workspace=web -> npm run start:web (or node build in apps/web).");
|
||||
info("Point WEB_ORIGIN / reverse proxy at that Node process; API stays on PUBLIC_API_URL.");
|
||||
info("Do not deploy as static-CDN-only - hooks.server.ts + server routes need Node.");
|
||||
info("See docs/production-checklist.md section 0 and docs/production-readiness.md deploy note.");
|
||||
}
|
||||
|
||||
console.log("cutover-deploy-check (read-only; no infra changes)");
|
||||
checkGoose();
|
||||
await checkReadyz();
|
||||
checkAdapterNodeNotes();
|
||||
|
||||
section("summary");
|
||||
if (failures.length === 0) {
|
||||
console.log("ok - goose 039+040+041+042 + worker heartbeat gates green; adapter-node host gates green.");
|
||||
console.log(
|
||||
"Reminder: this does not clear full Prod GO (Clerk/SMTP/Stripe/DNS/orphan run still ops).",
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
console.log(`${failures.length} failure(s):`);
|
||||
for (const f of failures) console.log(` - ${f}`);
|
||||
process.exit(1);
|
||||
@@ -0,0 +1,271 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Local cutover rehearsal (read-only / dry-run).
|
||||
*
|
||||
* Runs (in order):
|
||||
* 1) cutover-deploy-check (goose 039+040+041+042, /readyz, adapter-node notes)
|
||||
* 2) migrator -list-legacy-emails (no patch / export)
|
||||
* 3) migrator -list-member-memberships
|
||||
* 4) migrator -list-companies-without-plans
|
||||
* 5) orphan-processed cleanup POST without confirm (report-only)
|
||||
*
|
||||
* Excludes Clerk patch, SMTP/mailhooks, Stripe, assign/promote -confirm writes.
|
||||
* Assign / promote / orphan live deletes stay separate operator commands
|
||||
* (-confirm / confirm=true). This script never passes those flags.
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/cutover-local-rehearsal.mjs
|
||||
* node scripts/cutover-local-rehearsal.mjs --skip-deploy-check
|
||||
* node scripts/cutover-local-rehearsal.mjs --skip-orphan
|
||||
* make cutover-rehearsal
|
||||
*
|
||||
* Orphan step needs a platform-admin session: set DESCRYBE_SMOKE_PASS (or
|
||||
* CUTOVER_REHEARSAL_PASS) and optional CUTOVER_REHEARSAL_EMAIL (default
|
||||
* demo@descrybe.local). See docs/demo-user.md.
|
||||
*/
|
||||
import { spawnSync } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { DEV_API_PORT, DEV_PUBLIC_API_URL } from "./dev-ports.mjs";
|
||||
import { loadRootEnv, repoRoot } from "./root-env.mjs";
|
||||
|
||||
const args = new Set(process.argv.slice(2));
|
||||
const wantHelp = args.has("-h") || args.has("--help");
|
||||
const skipDeploy = args.has("--skip-deploy-check");
|
||||
const skipLists = args.has("--skip-lists");
|
||||
const skipOrphan = args.has("--skip-orphan");
|
||||
const deployPassthrough = [];
|
||||
if (args.has("--skip-goose")) deployPassthrough.push("--skip-goose");
|
||||
if (args.has("--skip-readyz")) deployPassthrough.push("--skip-readyz");
|
||||
|
||||
if (wantHelp) {
|
||||
console.log(`Usage: node scripts/cutover-local-rehearsal.mjs [flags]
|
||||
|
||||
Local dry-run rehearsal (no Clerk patch / SMTP / Stripe).
|
||||
--skip-deploy-check skip scripts/cutover-deploy-check.mjs
|
||||
--skip-lists skip migrator list-* steps
|
||||
--skip-orphan skip orphan-processed dry-run
|
||||
--skip-goose forward to deploy-check
|
||||
--skip-readyz forward to deploy-check
|
||||
|
||||
Env: DATABASE_URL (lists), PUBLIC_API_URL / HEALTH_BASE_URL (orphan),
|
||||
DESCRYBE_SMOKE_PASS or CUTOVER_REHEARSAL_PASS (+ optional CUTOVER_REHEARSAL_EMAIL).
|
||||
Never passes -confirm / confirm=true (assign/promote/orphan deletes stay separate).`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const env = loadRootEnv(process.env);
|
||||
const apiDir = path.join(repoRoot, "apps", "api");
|
||||
const failures = [];
|
||||
|
||||
function section(title) {
|
||||
console.log(`\n==> ${title}`);
|
||||
}
|
||||
|
||||
function pass(msg) {
|
||||
console.log(`PASS ${msg}`);
|
||||
}
|
||||
|
||||
function fail(msg) {
|
||||
console.log(`FAIL ${msg}`);
|
||||
failures.push(msg);
|
||||
}
|
||||
|
||||
function info(msg) {
|
||||
console.log(`NOTE ${msg}`);
|
||||
}
|
||||
|
||||
function run(cmd, argv, opts = {}) {
|
||||
const r = spawnSync(cmd, argv, {
|
||||
cwd: opts.cwd ?? repoRoot,
|
||||
env,
|
||||
encoding: "utf8",
|
||||
shell: process.platform === "win32",
|
||||
stdio: "inherit",
|
||||
});
|
||||
if (r.error) {
|
||||
fail(`${opts.label ?? cmd}: spawn failed: ${r.error.message}`);
|
||||
return false;
|
||||
}
|
||||
if (r.status !== 0) {
|
||||
fail(`${opts.label ?? cmd}: exited ${r.status}`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function runMigratorList(flag, label) {
|
||||
section(label);
|
||||
if (!env.DATABASE_URL) {
|
||||
fail(`DATABASE_URL unset - cannot run ${flag}`);
|
||||
return;
|
||||
}
|
||||
const ok = run(
|
||||
"go",
|
||||
["run", "./cmd/migrator", flag, "-postgres", env.DATABASE_URL],
|
||||
{ cwd: apiDir, label: flag },
|
||||
);
|
||||
if (ok) pass(`${flag} ok`);
|
||||
}
|
||||
|
||||
function cookieMapFromResponse(res, jar) {
|
||||
const raw =
|
||||
typeof res.headers.getSetCookie === "function"
|
||||
? res.headers.getSetCookie()
|
||||
: [];
|
||||
const single = res.headers.get("set-cookie");
|
||||
const lines = raw.length > 0 ? raw : single ? [single] : [];
|
||||
for (const line of lines) {
|
||||
const part = String(line).split(";")[0];
|
||||
const eq = part.indexOf("=");
|
||||
if (eq <= 0) continue;
|
||||
jar.set(part.slice(0, eq).trim(), part.slice(eq + 1).trim());
|
||||
}
|
||||
return jar;
|
||||
}
|
||||
|
||||
function cookieHeader(jar) {
|
||||
return [...jar.entries()].map(([k, v]) => `${k}=${v}`).join("; ");
|
||||
}
|
||||
|
||||
async function platformLogin(base) {
|
||||
const email =
|
||||
env.CUTOVER_REHEARSAL_EMAIL || env.DEMO_EMAIL || "demo@descrybe.local";
|
||||
const password = env.CUTOVER_REHEARSAL_PASS || env.DESCRYBE_SMOKE_PASS || "";
|
||||
if (!password) {
|
||||
return {
|
||||
ok: false,
|
||||
reason:
|
||||
"set DESCRYBE_SMOKE_PASS or CUTOVER_REHEARSAL_PASS for platform-admin orphan dry-run",
|
||||
};
|
||||
}
|
||||
const jar = new Map();
|
||||
const me = await fetch(`${base}/api/auth/me`, {
|
||||
headers: { Cookie: cookieHeader(jar) },
|
||||
});
|
||||
cookieMapFromResponse(me, jar);
|
||||
const csrf = jar.get("descrybe_csrf");
|
||||
if (!csrf) {
|
||||
return { ok: false, reason: "missing descrybe_csrf cookie from /api/auth/me" };
|
||||
}
|
||||
const login = await fetch(`${base}/api/auth/login`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-CSRF-Token": csrf,
|
||||
Cookie: cookieHeader(jar),
|
||||
},
|
||||
body: JSON.stringify({ email, password }),
|
||||
});
|
||||
cookieMapFromResponse(login, jar);
|
||||
const text = await login.text();
|
||||
if (!login.ok) {
|
||||
return {
|
||||
ok: false,
|
||||
reason: `login ${login.status}: ${text.slice(0, 200)}`,
|
||||
};
|
||||
}
|
||||
return { ok: true, jar, email };
|
||||
}
|
||||
|
||||
async function orphanDryRun() {
|
||||
section("orphan-processed dry-run (no confirm)");
|
||||
if (skipOrphan) {
|
||||
info("skipped (--skip-orphan)");
|
||||
return;
|
||||
}
|
||||
const base = (
|
||||
env.HEALTH_BASE_URL ||
|
||||
env.PUBLIC_API_URL ||
|
||||
DEV_PUBLIC_API_URL ||
|
||||
`http://127.0.0.1:${DEV_API_PORT}`
|
||||
).replace(/\/$/, "");
|
||||
|
||||
let session;
|
||||
try {
|
||||
session = await platformLogin(base);
|
||||
} catch (err) {
|
||||
fail(
|
||||
`orphan login failed: ${err instanceof Error ? err.message : err} (is API up?)`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!session.ok) {
|
||||
fail(`orphan dry-run skipped: ${session.reason}`);
|
||||
info(
|
||||
`Manual: GET ${base}/api/admin/jobs/orphan-processed or POST …/orphan-processed-cleanup (no confirm=true)`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const csrf = session.jar.get("descrybe_csrf");
|
||||
const url = `${base}/api/admin/jobs/orphan-processed-cleanup`;
|
||||
let res;
|
||||
let text;
|
||||
try {
|
||||
res = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-CSRF-Token": csrf || "",
|
||||
Cookie: cookieHeader(session.jar),
|
||||
},
|
||||
body: "{}",
|
||||
});
|
||||
text = await res.text();
|
||||
} catch (err) {
|
||||
fail(`orphan POST failed: ${err instanceof Error ? err.message : err}`);
|
||||
return;
|
||||
}
|
||||
console.log(`POST ${url} -> ${res.status}`);
|
||||
console.log(text);
|
||||
if (!res.ok) {
|
||||
fail(`orphan cleanup HTTP ${res.status}`);
|
||||
return;
|
||||
}
|
||||
pass(`orphan dry-run report-only (as ${session.email}; no confirm)`);
|
||||
}
|
||||
|
||||
console.log(
|
||||
"cutover-local-rehearsal (deploy-check + list-* + orphan dry-run; no Clerk/SMTP/Stripe)",
|
||||
);
|
||||
|
||||
if (!skipDeploy) {
|
||||
section("deploy-check");
|
||||
const ok = run(
|
||||
"node",
|
||||
[path.join("scripts", "cutover-deploy-check.mjs"), ...deployPassthrough],
|
||||
{ label: "cutover-deploy-check" },
|
||||
);
|
||||
if (ok) pass("deploy-check ok");
|
||||
} else {
|
||||
section("deploy-check");
|
||||
info("skipped (--skip-deploy-check)");
|
||||
}
|
||||
|
||||
if (!skipLists) {
|
||||
runMigratorList("-list-legacy-emails", "list-legacy-emails (no patch)");
|
||||
runMigratorList("-list-member-memberships", "list-member-memberships");
|
||||
runMigratorList(
|
||||
"-list-companies-without-plans",
|
||||
"list-companies-without-plans",
|
||||
);
|
||||
} else {
|
||||
section("migrator lists");
|
||||
info("skipped (--skip-lists)");
|
||||
}
|
||||
|
||||
await orphanDryRun();
|
||||
|
||||
section("summary");
|
||||
if (failures.length === 0) {
|
||||
console.log(
|
||||
"ok - local rehearsal green (lists + deploy-check + orphan dry-run).",
|
||||
);
|
||||
console.log(
|
||||
"Not included: Clerk patch, SMTP/mailhooks, Stripe, assign/promote -confirm.",
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
console.log(`${failures.length} failure(s):`);
|
||||
for (const f of failures) console.log(` - ${f}`);
|
||||
process.exit(1);
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Single source of truth for local `npm run dev` ports.
|
||||
* Chosen to avoid Cursor/agent leftovers on 5173/5174/5188/8080/18080.
|
||||
*/
|
||||
export const DEV_API_PORT = 28471;
|
||||
export const DEV_WEB_PORT = 28472;
|
||||
|
||||
export const DEV_HTTP_ADDR = `:${DEV_API_PORT}`;
|
||||
export const DEV_WEB_ORIGIN = `http://localhost:${DEV_WEB_PORT}`;
|
||||
export const DEV_PUBLIC_API_URL = `http://localhost:${DEV_API_PORT}`;
|
||||
export const DEV_API_PROXY_TARGET = `http://127.0.0.1:${DEV_API_PORT}`;
|
||||
@@ -0,0 +1,48 @@
|
||||
# Start Go API + SvelteKit web + worker from repo root (Windows PowerShell alternative to `npm run dev`).
|
||||
# Loads monorepo-root .env into this process so child go processes inherit it.
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$Root = Resolve-Path (Join-Path $PSScriptRoot "..")
|
||||
|
||||
$envFile = Join-Path $Root ".env"
|
||||
if (Test-Path $envFile) {
|
||||
Get-Content $envFile | ForEach-Object {
|
||||
if ($_ -match '^\s*#' -or $_ -match '^\s*$') { return }
|
||||
$line = $_
|
||||
if ($line -match '^\s*export\s+') { $line = $line -replace '^\s*export\s+', '' }
|
||||
$kv = $line -split '=', 2
|
||||
if ($kv.Length -eq 2) {
|
||||
$name = $kv[0].Trim()
|
||||
if (-not [string]::IsNullOrWhiteSpace($name) -and -not (Test-Path "Env:$name")) {
|
||||
Set-Item -Path "Env:$name" -Value $kv[1].Trim().Trim('"').Trim("'")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Free unique ports then force them (keep in sync with scripts/dev-ports.mjs)
|
||||
& node (Join-Path $Root "scripts\free-dev-ports.mjs")
|
||||
if ($LASTEXITCODE -ne 0) { throw "free-dev-ports.mjs failed" }
|
||||
|
||||
$env:DESCRYBE_DEV_PORTS_FREED = "1"
|
||||
$env:HTTP_ADDR = ":28471"
|
||||
$env:WEB_ORIGIN = "http://localhost:28472"
|
||||
$env:PUBLIC_API_URL = "http://localhost:28471"
|
||||
|
||||
$api = Start-Process -PassThru -NoNewWindow -WorkingDirectory (Join-Path $Root "apps\api") -FilePath "go" -ArgumentList @("run", "./cmd/api")
|
||||
$worker = Start-Process -PassThru -NoNewWindow -WorkingDirectory (Join-Path $Root "apps\api") -FilePath "go" -ArgumentList @("run", "./cmd/worker")
|
||||
$web = Start-Process -PassThru -NoNewWindow -WorkingDirectory $Root -FilePath "npm" -ArgumentList @("run", "dev", "--workspace=web")
|
||||
|
||||
function Stop-Children {
|
||||
foreach ($p in @($api, $worker, $web)) {
|
||||
if ($null -ne $p -and -not $p.HasExited) {
|
||||
Stop-Process -Id $p.Id -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Wait-Process -Id @($api.Id, $worker.Id, $web.Id)
|
||||
} finally {
|
||||
Stop-Children
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Kill whatever is listening on the Descrybe dev ports so `npm run dev` can bind.
|
||||
* Skip when DESCRYBE_DEV_PORTS_FREED=1 (parent already freed; children must not
|
||||
* re-kill the just-started sibling).
|
||||
*
|
||||
* Usage: node scripts/free-dev-ports.mjs
|
||||
*/
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { DEV_API_PORT, DEV_WEB_PORT } from "./dev-ports.mjs";
|
||||
|
||||
export function freeDevPorts(ports = [DEV_API_PORT, DEV_WEB_PORT]) {
|
||||
if (process.env.DESCRYBE_DEV_PORTS_FREED === "1") {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const port of ports) {
|
||||
const pids = listenersOnPort(port);
|
||||
if (pids.length === 0) {
|
||||
console.log(`[dev-ports] ${port} free`);
|
||||
continue;
|
||||
}
|
||||
for (const pid of pids) {
|
||||
console.log(`[dev-ports] killing PID ${pid} on :${port}`);
|
||||
killPidTree(pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function listenersOnPort(port) {
|
||||
if (process.platform === "win32") {
|
||||
const ps = [
|
||||
`$conns = @(Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue);`,
|
||||
`if ($conns.Count -eq 0) { exit 0 };`,
|
||||
`$conns | ForEach-Object { $_.OwningProcess } | Sort-Object -Unique | ForEach-Object { Write-Output $_ }`,
|
||||
].join(" ");
|
||||
const r = spawnSync(
|
||||
"powershell.exe",
|
||||
["-NoProfile", "-NonInteractive", "-Command", ps],
|
||||
{ encoding: "utf8" },
|
||||
);
|
||||
return parsePids(r.stdout || "");
|
||||
}
|
||||
|
||||
const r = spawnSync("lsof", ["-tiTCP:" + port, "-sTCP:LISTEN"], {
|
||||
encoding: "utf8",
|
||||
});
|
||||
if (r.status !== 0) return [];
|
||||
return parsePids(r.stdout || "");
|
||||
}
|
||||
|
||||
function parsePids(text) {
|
||||
const seen = new Set();
|
||||
const out = [];
|
||||
for (const raw of text.split(/\r?\n/)) {
|
||||
const n = Number.parseInt(raw.trim(), 10);
|
||||
if (!Number.isFinite(n) || n <= 0 || seen.has(n)) continue;
|
||||
if (n === process.pid) continue;
|
||||
seen.add(n);
|
||||
out.push(n);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function killPidTree(pid) {
|
||||
if (process.platform === "win32") {
|
||||
spawnSync("taskkill.exe", ["/T", "/F", "/PID", String(pid)], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
process.kill(pid, "SIGKILL");
|
||||
} catch {
|
||||
/* already gone */
|
||||
}
|
||||
}
|
||||
|
||||
const isDirect =
|
||||
process.argv[1] &&
|
||||
import.meta.url === pathToFileURL(process.argv[1]).href;
|
||||
|
||||
if (isDirect) {
|
||||
freeDevPorts();
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Probe API /healthz and /readyz (worker heartbeat required for ready).
|
||||
* Usage: node scripts/health.mjs
|
||||
*/
|
||||
import { DEV_API_PORT, DEV_PUBLIC_API_URL } from "./dev-ports.mjs";
|
||||
import { loadRootEnv } from "./root-env.mjs";
|
||||
|
||||
const env = loadRootEnv(process.env);
|
||||
const base = (
|
||||
env.PUBLIC_API_URL ||
|
||||
env.HEALTH_BASE_URL ||
|
||||
DEV_PUBLIC_API_URL ||
|
||||
`http://127.0.0.1:${DEV_API_PORT}`
|
||||
).replace(/\/$/, "");
|
||||
|
||||
async function probe(path) {
|
||||
const url = `${base}${path}`;
|
||||
const res = await fetch(url);
|
||||
const text = await res.text();
|
||||
console.log(`${path} → ${res.status}`);
|
||||
console.log(text);
|
||||
if (!res.ok) {
|
||||
try {
|
||||
const body = JSON.parse(text);
|
||||
if (typeof body?.reason === "string" && body.reason) {
|
||||
console.error(`reason: ${body.reason}`);
|
||||
}
|
||||
} catch {
|
||||
/* ignore non-JSON */
|
||||
}
|
||||
throw new Error(`${path} failed with ${res.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await probe("/healthz");
|
||||
await probe("/readyz");
|
||||
console.log("ok");
|
||||
} catch (err) {
|
||||
console.error(err instanceof Error ? err.message : err);
|
||||
console.error(
|
||||
"Tip: npm run dev (or npm run dev:backend) starts api+worker. Compose is Postgres-only; /readyz needs a live host worker heartbeat. API-only (npm run dev:api) → 503 is expected.",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Cross-platform goose migrate + sqlc generate (Windows / macOS / Linux).
|
||||
* Loads monorepo-root `.env` when DATABASE_URL is unset.
|
||||
*
|
||||
* Usage: node scripts/migrate.mjs
|
||||
*/
|
||||
import { spawnSync } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { loadRootEnv, repoRoot } from "./root-env.mjs";
|
||||
|
||||
const env = loadRootEnv(process.env);
|
||||
if (!env.DATABASE_URL) {
|
||||
console.error(
|
||||
"DATABASE_URL is required (set in monorepo-root .env or the shell; see .env.example)",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const apiDir = path.join(repoRoot, "apps", "api");
|
||||
|
||||
function run(command, args, opts = {}) {
|
||||
const r = spawnSync(command, args, {
|
||||
cwd: apiDir,
|
||||
env,
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
...opts,
|
||||
});
|
||||
if (r.error) {
|
||||
console.error(r.error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
if (r.status !== 0) {
|
||||
process.exit(r.status ?? 1);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("==> goose up (apps/api/sql/schema)");
|
||||
run("go", [
|
||||
"run",
|
||||
"github.com/pressly/goose/v3/cmd/goose@v3.24.3",
|
||||
"-dir",
|
||||
"sql/schema",
|
||||
"postgres",
|
||||
env.DATABASE_URL,
|
||||
"up",
|
||||
]);
|
||||
|
||||
console.log("==> sqlc generate");
|
||||
const sqlc = spawnSync("sqlc", ["generate"], {
|
||||
cwd: apiDir,
|
||||
env,
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
});
|
||||
if (sqlc.status !== 0) {
|
||||
console.log("sqlc not on PATH (or failed); using go run");
|
||||
run("go", [
|
||||
"run",
|
||||
"github.com/sqlc-dev/sqlc/cmd/sqlc@v1.28.0",
|
||||
"generate",
|
||||
]);
|
||||
}
|
||||
|
||||
console.log("==> done");
|
||||
@@ -0,0 +1,5 @@
|
||||
# Thin wrapper — prefer: npm run migrate (or node scripts/migrate.mjs)
|
||||
$ErrorActionPreference = "Stop"
|
||||
$Root = Resolve-Path (Join-Path $PSScriptRoot "..")
|
||||
& node (Join-Path $Root "scripts\migrate.mjs")
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Thin wrapper — prefer: npm run migrate (or node scripts/migrate.mjs)
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
exec node "$ROOT/scripts/migrate.mjs"
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Shared helpers for monorepo-root `.env` (never commit secrets).
|
||||
*/
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
export const repoRoot = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
"..",
|
||||
);
|
||||
|
||||
export function dotenvPath() {
|
||||
return process.env.DOTENV_PATH
|
||||
? path.resolve(process.env.DOTENV_PATH)
|
||||
: path.join(repoRoot, ".env");
|
||||
}
|
||||
|
||||
/** Parse KEY=VAL lines; does not expand shell. */
|
||||
export function parseEnvFile(text) {
|
||||
const out = {};
|
||||
for (const raw of text.split(/\r?\n/)) {
|
||||
let line = raw.trim();
|
||||
if (!line || line.startsWith("#")) continue;
|
||||
if (line.startsWith("export ")) line = line.slice(7).trim();
|
||||
const eq = line.indexOf("=");
|
||||
if (eq <= 0) continue;
|
||||
const key = line.slice(0, eq).trim();
|
||||
if (!key) continue;
|
||||
let val = line.slice(eq + 1).trim();
|
||||
if (
|
||||
(val.startsWith('"') && val.endsWith('"')) ||
|
||||
(val.startsWith("'") && val.endsWith("'"))
|
||||
) {
|
||||
val = val.slice(1, -1);
|
||||
}
|
||||
out[key] = val;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** Merge root `.env` into env (does not override existing keys). */
|
||||
export function loadRootEnv(env = process.env) {
|
||||
const file = dotenvPath();
|
||||
let text;
|
||||
try {
|
||||
text = fs.readFileSync(file, "utf8");
|
||||
} catch {
|
||||
return { ...env };
|
||||
}
|
||||
const parsed = parseEnvFile(text);
|
||||
const out = { ...env };
|
||||
for (const [key, val] of Object.entries(parsed)) {
|
||||
if (Object.prototype.hasOwnProperty.call(out, key)) continue;
|
||||
out[key] = val;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export function readRootEnvFile() {
|
||||
const file = dotenvPath();
|
||||
try {
|
||||
return fs.readFileSync(file, "utf8");
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function writeRootEnvFile(text) {
|
||||
fs.writeFileSync(dotenvPath(), text, "utf8");
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
# Load monorepo-root .env and run the API or worker (go run).
|
||||
# Usage: .\scripts\run-api.ps1 [worker]
|
||||
$ErrorActionPreference = "Stop"
|
||||
$Root = Resolve-Path (Join-Path $PSScriptRoot "..")
|
||||
$ApiDir = Join-Path $Root "apps\api"
|
||||
$envFile = Join-Path $Root ".env"
|
||||
if (Test-Path $envFile) {
|
||||
Get-Content $envFile | ForEach-Object {
|
||||
if ($_ -match '^\s*#' -or $_ -match '^\s*$') { return }
|
||||
$line = $_
|
||||
if ($line -match '^\s*export\s+') { $line = $line -replace '^\s*export\s+', '' }
|
||||
$kv = $line -split '=', 2
|
||||
if ($kv.Length -eq 2) {
|
||||
$name = $kv[0].Trim()
|
||||
if (-not [string]::IsNullOrWhiteSpace($name) -and -not (Test-Path "Env:$name")) {
|
||||
Set-Item -Path "Env:$name" -Value $kv[1].Trim().Trim('"').Trim("'")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Set-Location $ApiDir
|
||||
if ($args -contains "worker") {
|
||||
go run ./cmd/worker
|
||||
} else {
|
||||
go run ./cmd/api
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Cross-platform: run a command with cwd set to a repo-relative directory.
|
||||
* Loads monorepo-root `.env` into the child env (does not override existing vars).
|
||||
* Usage: node scripts/run-in-dir.mjs <relDir> <command> [args...]
|
||||
*/
|
||||
import { spawn } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const [relDir, ...cmd] = process.argv.slice(2);
|
||||
|
||||
if (!relDir || cmd.length === 0) {
|
||||
console.error("Usage: node scripts/run-in-dir.mjs <relDir> <command> [args...]");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function loadRootEnv(env) {
|
||||
const dotenvPath = process.env.DOTENV_PATH
|
||||
? path.resolve(process.env.DOTENV_PATH)
|
||||
: path.join(root, ".env");
|
||||
let text;
|
||||
try {
|
||||
text = fs.readFileSync(dotenvPath, "utf8");
|
||||
} catch {
|
||||
return env;
|
||||
}
|
||||
const out = { ...env };
|
||||
for (const raw of text.split(/\r?\n/)) {
|
||||
let line = raw.trim();
|
||||
if (!line || line.startsWith("#")) continue;
|
||||
if (line.startsWith("export ")) line = line.slice(7).trim();
|
||||
const eq = line.indexOf("=");
|
||||
if (eq <= 0) continue;
|
||||
const key = line.slice(0, eq).trim();
|
||||
if (!key || Object.prototype.hasOwnProperty.call(out, key)) continue;
|
||||
let val = line.slice(eq + 1).trim();
|
||||
if (
|
||||
(val.startsWith('"') && val.endsWith('"')) ||
|
||||
(val.startsWith("'") && val.endsWith("'"))
|
||||
) {
|
||||
val = val.slice(1, -1);
|
||||
}
|
||||
out[key] = val;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const cwd = path.join(root, relDir);
|
||||
const [command, ...args] = cmd;
|
||||
const child = spawn(command, args, {
|
||||
cwd,
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
env: loadRootEnv(process.env),
|
||||
});
|
||||
|
||||
child.on("exit", (code, signal) => {
|
||||
if (signal) {
|
||||
process.kill(process.pid, signal);
|
||||
return;
|
||||
}
|
||||
process.exit(code ?? 1);
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Seed / reset local demo login accounts (cross-platform).
|
||||
* Requires Postgres + DATABASE_URL (defaults to docker-compose URL).
|
||||
*
|
||||
* Usage: node scripts/seed-local.mjs
|
||||
*/
|
||||
import { spawnSync } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { DEV_API_PORT, DEV_WEB_PORT } from "./dev-ports.mjs";
|
||||
import { loadRootEnv, repoRoot } from "./root-env.mjs";
|
||||
|
||||
const env = loadRootEnv(process.env);
|
||||
if (!env.DATABASE_URL) {
|
||||
env.DATABASE_URL =
|
||||
"postgres://descrybe:descrybe@localhost:5433/descrybe?sslmode=disable";
|
||||
}
|
||||
|
||||
const demoEmail = env.SEED_DEMO_EMAIL || "demo@descrybe.local";
|
||||
const demoPass = env.SEED_DEMO_PASSWORD || "DemoPass123!";
|
||||
const adminEmail = env.SEED_ADMIN_EMAIL || "berce.tim@gmail.com";
|
||||
const adminPass = env.SEED_ADMIN_PASSWORD || "LocalDemo!Admin1";
|
||||
|
||||
const apiDir = path.join(repoRoot, "apps", "api");
|
||||
|
||||
function run(args) {
|
||||
const r = spawnSync("go", args, {
|
||||
cwd: apiDir,
|
||||
env,
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
});
|
||||
if (r.status !== 0) process.exit(r.status ?? 1);
|
||||
}
|
||||
|
||||
console.log(`==> set-password ${adminEmail} (platform admin if present)`);
|
||||
run([
|
||||
"run",
|
||||
"./cmd/migrator",
|
||||
"-postgres",
|
||||
env.DATABASE_URL,
|
||||
"-set-password",
|
||||
`${adminEmail}:${adminPass}`,
|
||||
]);
|
||||
|
||||
console.log(
|
||||
"==> seed-demo (also upserts demo@descrybe.test)",
|
||||
);
|
||||
run([
|
||||
"run",
|
||||
"./cmd/seed-demo",
|
||||
"-postgres",
|
||||
env.DATABASE_URL,
|
||||
"-email",
|
||||
demoEmail,
|
||||
"-password",
|
||||
demoPass,
|
||||
"-also-email",
|
||||
"demo@descrybe.test",
|
||||
]);
|
||||
|
||||
console.log("");
|
||||
console.log("Local accounts:");
|
||||
console.log(` demo: ${demoEmail} / ${demoPass} (also demo@descrybe.test)`);
|
||||
console.log(` admin: ${adminEmail} / ${adminPass} (platform admin when that user exists)`);
|
||||
console.log(" plan: Enterprise — Local Demo Co");
|
||||
console.log(`Web: http://localhost:${DEV_WEB_PORT}/login`);
|
||||
console.log(`API: http://localhost:${DEV_API_PORT}`);
|
||||
@@ -0,0 +1,5 @@
|
||||
# Thin wrapper — prefer: npm run seed (or node scripts/seed-local.mjs)
|
||||
$ErrorActionPreference = "Stop"
|
||||
$Root = Resolve-Path (Join-Path $PSScriptRoot "..")
|
||||
& node (Join-Path $Root "scripts\seed-local.mjs")
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
@@ -0,0 +1,105 @@
|
||||
A1 Slovenija tenant seed (Postgres COPY archive, gzipped)
|
||||
|
||||
File: a1-demo-data.sql.gz
|
||||
Company id: 604f23a8-b66e-4b21-8b45-0d72b68f4790
|
||||
Legacy MySQL id: 97e1a309-3d23-4aa2-b518-8e8d7afdfec7
|
||||
|
||||
Source of truth for a clean local DB (2026-08-08 reseed):
|
||||
MySQL dump → C:\Users\Green Eclipse\Downloads\descrybe_new (1).sql
|
||||
Loaded into Laragon MySQL DB `descrybe_new`, then A1-only migrator
|
||||
(`-company 97e1a309-3d23-4aa2-b518-8e8d7afdfec7`) + seed-demo + user cleanup.
|
||||
See docs/migrate-from-descrybe-new.md and docs/demo-user.md.
|
||||
|
||||
Expected A1 snapshot (dump-faithful, no local QA/Iso pollution):
|
||||
companies=1 (A1 Slovenija only, plus internal __platform_settings__)
|
||||
users=3 (demo@descrybe.local, demo@descrybe.test, a1-primary@descrybe.local)
|
||||
memberships=3
|
||||
categories=119 (108 with AI prompts via overlay)
|
||||
attributes=303
|
||||
category_attributes=307
|
||||
input_feeds=12 / feed_mappings=11 / export_feeds=2
|
||||
raw_products=~25355 / processed_products=0 (demo seed clears processed)
|
||||
processing_jobs=0 / processing_job_products=0
|
||||
plan=A1 wallet total=2500 used=163 remaining=2337
|
||||
mapped coverage (typical): description ~98%, feed attrs ~99%, category ~17% (4330 dump-assigned)
|
||||
|
||||
Category assignment (dump -> PG):
|
||||
MySQL processed_products.category holds category unique_id (e.g. 46, 120).
|
||||
Feed XML / feed_mappings do NOT include a category field - assignment was
|
||||
historical processing output. Archive restores raw mapped_data (description,
|
||||
specs/attrs, category when present). seed-a1 then refreshes
|
||||
mapped_data.category from a MySQL dump when available.
|
||||
|
||||
Dump path (first match wins):
|
||||
1. -mysql-dump flag
|
||||
2. SEED_A1_MYSQL_DUMP
|
||||
3. Auto-detect: ~/Downloads/descrybe_new (1).sql or descrybe_new.sql
|
||||
|
||||
One-off without wipe:
|
||||
go run ./cmd/seed-a1 -mode backfill-categories
|
||||
|
||||
That also deletes Postman Elkotex fixture EANs (5905575903198, 6970995789942)
|
||||
from non-A1 tenants (Platform Demo must not mirror them).
|
||||
|
||||
Note: MySQL processed description/attributes are usually NULL; feed-origin
|
||||
original description + specs live on raw_products.mapped_data in the archive.
|
||||
A1 demo seed keeps processed=0 / jobs=0.
|
||||
|
||||
Who sees what:
|
||||
demo@descrybe.local � platform admin + membership on Platform Demo only
|
||||
(switch/impersonate to a1-primary to act on A1; do not use demo API key for A1)
|
||||
a1-primary@descrybe.local � A1 member only (same A1 catalog; no other tenants)
|
||||
|
||||
Category prompts overlay (committed JSON from legacy wp_product_categories):
|
||||
a1-category-prompts.json
|
||||
Applied automatically after every `npm run seed:a1` reimport (match by normalized
|
||||
category name; rewrites {""OPIS IZDELKA""} ? {{description}},
|
||||
{""STARO IME IZDELKA""} ? {{name}}). Skip with -skip-category-prompts, or re-apply
|
||||
without wipe:
|
||||
|
||||
go run ./cmd/seed-a1 -mode apply-category-prompts -file ../../scripts/seed/a1-demo-data.sql.gz
|
||||
|
||||
Normal use (loads DATABASE_URL from repo-root .env if unset):
|
||||
npm run seed:a1 # wipe A1-only rows, import archive, prompts + mapped category backfill
|
||||
npm run reimport:a1 # alias of seed:a1
|
||||
|
||||
Requires migrations already applied. Does not wipe other companies.
|
||||
Does not run export — it only imports scripts/seed/a1-demo-data.sql.gz.
|
||||
|
||||
A1 demo seed skips processed_products / processing_jobs (processed=0, jobs=0).
|
||||
Use -mode recover-jobs only when restoring legacy job history from a MySQL dump.
|
||||
feed_sync_jobs stay ephemeral and are not restored; raw_products.sync_job_id is
|
||||
exported as NULL for that reason. Archive is Postgres COPY text inside gzip, not
|
||||
MySQL dump SQL.
|
||||
|
||||
Full clean rebuild from MySQL dump (when archive is wrong / polluted):
|
||||
1. mysql: DROP/CREATE descrybe_new; stream-import the Adminer dump
|
||||
2. docker: DROP/CREATE postgres descrybe; goose up
|
||||
3. migrator -company <A1 legacy id> -resume -ensure-demo -skip-post-import
|
||||
4. migrator -domains jobs -resume
|
||||
5. go run ./cmd/seed-demo
|
||||
6. keep only demo + a1-primary memberships; delete orphan users / Iso test cos
|
||||
7. go run ./cmd/seed-a1 -mode apply-category-prompts
|
||||
8. npm run seed:a1:export # refresh a1-demo-data.sql.gz then commit
|
||||
|
||||
Export copies EVERY A1 processing_jobs / processing_job_products row (no LIMIT).
|
||||
If the archive only has a couple of jobs, live Postgres was missing history —
|
||||
recover from the MySQL dump first, then re-export:
|
||||
|
||||
set SEED_A1_MYSQL_DUMP=D:\path\descrybe_new.sql
|
||||
go run ./cmd/seed-a1 -mode recover-jobs
|
||||
npm run seed:a1:export
|
||||
|
||||
Maintainer only — regenerate the snapshot when live A1 data must be re-captured:
|
||||
npm run seed:a1:export # snapshot live A1 → a1-demo-data.sql.gz (then commit)
|
||||
Not part of the day-to-day reset workflow.
|
||||
|
||||
Support Knowledge articles (platform FAQ /admin/support/knowledge):
|
||||
support-kb-articles.json � product/user corpus (Agent 6)
|
||||
support-kb-articles-tech.json � technical/ops/admin corpus (Agent 7; tech-* slugs)
|
||||
Markdown sources for tech bodies (optional edit then regenerate JSON):
|
||||
apps/api/cmd/seed-support-kb/content/*.md
|
||||
Seed (from apps/api, DATABASE_URL set):
|
||||
go run ./cmd/seed-support-kb
|
||||
go run ./cmd/seed-support-kb -file ../../scripts/seed/support-kb-articles-tech.json
|
||||
Idempotent ON CONFLICT (slug). Do not reuse product slugs for tech articles.
|
||||
@@ -0,0 +1,471 @@
|
||||
{
|
||||
"version": 1,
|
||||
"source": "wp_product_categories.sql",
|
||||
"company_hint": "A1 Slovenija",
|
||||
"entries": [
|
||||
{
|
||||
"name": "Agregati",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Avto oprema",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Avto polnilniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"vrsta priključka (npr. USB C)\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Avtosedeži in lupinice",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"skupina po teži [kg]\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka brez skupine po teži in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez skupne teže}</p>\n<H2>{Napiši Novo ime izdelka brez skupne teže in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez skupne teže}</p>\n<H2>{Napiši Novo ime izdelka brez skupne teže in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Bazeni",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"dimenzije [cm]\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka brez dimenzije in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez dimenzije}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Bluetooth zvočniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"barva v eni slovenski besedi\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka brez barve in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka brez barve in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Brezžični polnilniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Brivniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Cvrtniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\r\n\r\nStar_opis_izdelka: {\"OPIS IZDELKA\"};\r\nStaro_ime_izdelka: {\"STARO IME IZDELKA\"};\r\n\r\nUporabi spodnjo GPT predlogo.\r\n\r\nGPT predloga:\r\n\r\n<name>{Napiši novo ime izdelka po formuli: \"znamka s pravilno kapitalizacijo\", \"cvrtnik lowercase\", \"model izdelka uppercase\", \"kapaciteta v litrih\". Ne uporabljaj vejic.}</name>\r\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\r\n\r\n<H2>{Napiši Novo ime izdelka brez kapacitete in izpostavi en benefit}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in NE vsebuje Novo ime izdelka.}</p>\r\n<H2>{Izpostavi en benefit in NE napiši Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI Novo ime izdelka brez kapacitete}</p>\r\n<H2>{Napiši Novo ime izdelka brez kapacitete in izpostavi en benefit}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in NE vsebuje Novo ime izdelka.}</p>\r\n<H2>{Izpostavi en benefit in NE napiši Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI Novo ime izdelka brez kapacitete}</p>\r\n<H2>{Napiši Novo ime izdelka brez kapacitete in izpostavi en benefit}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in NE vsebuje Novo ime izdelka.}</p>\r\n\r\n<b>Tehnične specifikacije</b>\r\n<ul>\r\n<li>{Napiši 3–10 tehničnih specifikacij v alinejah}</li>\r\n</ul>"
|
||||
},
|
||||
{
|
||||
"name": "Depilatorji",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Digitalna peresa",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Dodatki za dom",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\r\n\r\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\r\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\r\n\r\nUporabi spodnjo GPT predlogo. \r\n\r\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\r\n\r\nGPT predloga:\r\n\r\n\r\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka lowercase\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\r\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\r\n\r\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\r\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in NE VKLJUČI Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Dodatki za televizorje",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Dodatki za ure",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic. Ne vključuj barve v ime.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Dodatna oprema",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Dodatna oprema za računalništvo",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Droni",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Družabne igre",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"ime igre\"\", \"\"model\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Električna kolesa",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Električne zobne ščetke",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Električni skiroji",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Fitnes oprema",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Gaming monitorji",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"SD / HD / Full HD / 2K / 4K / 8K\"\", \"\"monitor lowercase\"\", \"\"Stopnja osveževanja v Hz\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"diagonala v inčih in označeno z '.\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka brez diagonale in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka brez diagonale in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Gaming pohištvo",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Gaming prenosniki računalniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"procesor\"\", \"\"grafična kartica\"\", \"\"pomnilnik\"\", \"\"dimenzije\"\", \"\"osveževanje (če je 144Hz)\"\", \"\"SD / HD / FHD / UHD / 4K / 8K\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši tip izdelka sentence case in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi tip izdelka lowercase}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Gaming stacionarni računalniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"procesor\"\", \"\"grafična kartica\"\", \"\"pomnilnik\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši tip izdelka sentence case in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi tip izdelka lowercase}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Giljotine in rezalniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\r\n\r\nStar_opis_izdelka: {\"OPIS IZDELKA\"};\r\nStaro_ime_izdelka: {\"STARO IME IZDELKA\"};\r\n\r\nUporabi spodnjo GPT predlogo.\r\n\r\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\r\n\r\nGPT predloga:\r\n<name>{Napiši novo ime izdelka po formuli: \"tip izdelka (»Giljotina« ali »Rezalnik«)\", \"znamka s pravilno kapitalizacijo\", \"model izdelka uppercase\". Ne uporabljaj vejic.}</name>\r\n\r\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\r\n<H2>{Izpostavi en benefit in VKLJUČI Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in NE vsebuje Novo ime izdelka.}</p>\r\n<H2>{Izpostavi en benefit in VKLJUČI Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI Novo ime izdelka.}</p>\r\n<b>Tehnične specifikacije</b>\r\n<ul>\r\n<li>{Napiši 3–10 tehničnih specifikacij v alinejah}</li>\r\n</ul>"
|
||||
},
|
||||
{
|
||||
"name": "Grelniki vode",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\r\n\r\nStar_opis_izdelka: {\"OPIS IZDELKA\"};\r\nStaro_ime_izdelka: {\"STARO IME IZDELKA\"};\r\n\r\nUporabi spodnjo GPT predlogo.\r\n\r\nGPT predloga:\r\n\r\n<name>{Napiši novo ime izdelka po formuli: \"znamka s pravilno kapitalizacijo\", \"grelnik vode lowercase\", \"model izdelka uppercase\", \"kapaciteta v litrih\". Ne uporabljaj vejic.}</name>\r\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\r\n\r\n<H2>{Napiši Novo ime izdelka brez kapacitete in izpostavi en benefit}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in NE vsebuje Novo ime izdelka.}</p>\r\n<H2>{Izpostavi en benefit in NE napiši Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI Novo ime izdelka brez kapacitete}</p>\r\n<H2>{Napiši Novo ime izdelka brez kapacitete in izpostavi en benefit}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in NE vsebuje Novo ime izdelka.}</p>\r\n<H2>{Izpostavi en benefit in NE napiši Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI Novo ime izdelka brez kapacitete}</p>\r\n<H2>{Napiši Novo ime izdelka brez kapacitete in izpostavi en benefit}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in NE vsebuje Novo ime izdelka.}</p>\r\n\r\n<b>Tehnične specifikacije</b>\r\n<ul>\r\n<li>{Napiši 3–10 tehničnih specifikacij v alinejah}</li>\r\n</ul>"
|
||||
},
|
||||
{
|
||||
"name": "Hišni polnilniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Hladilniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Igralne konzole in igre",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Igrače na daljinsko vodenje",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Igrače za najmlajše",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Kabli",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\r\n\r\nStar_opis_izdelka: {\"OPIS IZDELKA\"};\r\nStaro_ime_izdelka: {\"STARO IME IZDELKA\"};\r\n\r\nUporabi spodnjo GPT predlogo. \r\n\r\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\r\n\r\nGPT predloga:\r\n\r\n\r\n<name>{Napiši novo ime izdelka po formuli: \"vrsta kabla (UTP/STP…)\", \"kabel\", \"dolžina [m]\", \"znamka normal case\", \"model izdelka uppercase\". Če je kabel za polnjenje, potem sestavi ime tako: \"polnilni kabel\", \"znamka normal case\", \"model izdelka uppercase\". Ne uporabljaj vejic.}</name>\r\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\r\n<H1>{Zapiši Novo_ime_izdelka}</H1>\r\n<H2>{Naj vključuje Novo_ime_izdelka in izpostavi en benefit}</H2>\r\n<p>{Nadaljuj iz naslova in napiši 100 besed o temu benefitu, ne vključuj Novo_ime_izdelka}</p>"
|
||||
},
|
||||
{
|
||||
"name": "Kamere in fotoaparati",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Kamp oprema in aktivnosti na prostem",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\r\n\r\nStar_opis_izdelka: {\"OPIS IZDELKA\"};\r\nStaro_ime_izdelka: {\"STARO IME IZDELKA\"};\r\n\r\nUporabi spodnjo GPT predlogo.\r\n\r\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\r\n\r\nGPT predloga:\r\n\r\n<name>{Napiši novo ime izdelka po formuli: \"vrsta izdelka lowercase\", \"znamka s pravilno kapitalizacijo\", \"model izdelka\". Ne uporabljaj vejic.}</name>\r\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\r\n\r\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\r\n<H2>{Izpostavi en benefit in NE napiši Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI Novo ime izdelka}</p>\r\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\r\n<H2>{Izpostavi en benefit in NE napiši Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI Novo ime izdelka}</p>\r\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\r\n\r\n<b>Tehnične specifikacije</b>\r\n<ul>\r\n<li>{Napiši 3–10 tehničnih specifikacij v alinejah}</li>\r\n</ul>"
|
||||
},
|
||||
{
|
||||
"name": "Kavni aparati",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Klasična kolesa",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Klimatske naprave",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\r\n\r\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\r\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\r\n\r\nUporabi spodnjo GPT predlogo. \r\n\r\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\r\n\r\nGPT predloga:\r\n\r\n\r\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"klima z montažo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\r\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\r\n\r\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\r\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\r\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Kuhalne plošče",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Kuhinjske nape",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"širina [cm]\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka brez širine in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez širine}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Kuhinjski roboti",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Likalniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Masažni bazeni",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Mešalniki in blenderji",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Mikrovalovne pečice",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Miške",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Mobilne naprave",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"velikost pomnilnika\"\", \"\"barva v eni slovenski besedi\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka brez barve in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez barve}</p>\n<H2>{Napiši Novo ime izdelka brez barve in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez barve}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez barve}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Monitorji",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"SD / HD / Full HD / 2K / 4K / 8K\"\", \"\"Stopnja osveževanja v Hz\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"diagonala v inčih in označeno z '.\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka brez diagonale in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka brez diagonale in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Mrežna oprema",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Nadzorne kamere",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Namizni računalniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"tip procesorja\"\", \"\"tip grafične kartice\"\", \"\"pomnilnik\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka brez procesorja, brez grafične kartice, brez pomnilnika in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez procesorja, brez grafične kartice in brez pomnilnika}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Namizni žari in opekači",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Naprave za ogrevanje",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Nosilci za TV",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Oblikovalci in sušilniki za lase",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Obnovljeni prenosniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"procesor\"\", \"\"grafična kartica\"\", \"\"pomnilnik\"\", \"\"dimenzije\"\", \"\"osveževanje (če je 144Hz)\"\", \"\"SD / HD / FHD / UHD / 4K / 8K\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši tip izdelka sentence case in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi tip izdelka lowercase}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Obnovljeni telefoni",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"velikost pomnilnika\"\", barva v eni slovenski besedi\"\", \"\"obnovljen\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka brez barve in brez podatka o obnovljenosti in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez barve in brez podatka o obnovljenosti}</p>\n<H2>{Napiši Novo ime izdelka brez barve in brez podatka o obnovljenosti in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez barve in brez podatka o obnovljenosti}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez barve in brez podatka o obnovljenosti}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Orodje za mojstre",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Osebna nega",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Ostali aparati",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Ostali dodatki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka (lahko tudi brez, če ni primernega izraza) sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"barva v eni slovenski besedi (če je možnih več barv)\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka brez barve in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez barve}</p>\n<H2>{Napiši Novo ime izdelka brez barve in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Ostali gaming dodatki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Otroška vozila",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Otroške pametne ure",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\r\n\r\nStar_opis_izdelka: {\"OPIS IZDELKA\"};\r\nStaro_ime_izdelka: {\"STARO IME IZDELKA\"};\r\n\r\nUporabi spodnjo GPT predlogo.\r\n\r\nGPT predloga:\r\n\r\n<name>{Napiši novo ime izdelka po formuli: \"znamka s pravilno kapitalizacijo\", \"otroška pametna ura lowercase\", \"model izdelka uppercase\", \"barva\". Ne uporabljaj vejic.}</name>\r\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\r\n\r\n<H2>{Napiši Novo ime izdelka brez barve in izpostavi en benefit}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in NE vsebuje Novo ime izdelka.}</p>\r\n<H2>{Izpostavi en benefit in NE napiši Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI Novo ime izdelka brez barve}</p>\r\n<H2>{Napiši Novo ime izdelka brez barve in izpostavi en benefit}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in NE vsebuje Novo ime izdelka.}</p>\r\n<H2>{Izpostavi en benefit in NE napiši Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI Novo ime izdelka brez barve}</p>\r\n<H2>{Napiši Novo ime izdelka brez barve in izpostavi en benefit}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in NE vsebuje Novo ime izdelka.}</p>\r\n\r\n<b>Tehnične specifikacije</b>\r\n<ul>\r\n<li>{Napiši 3–10 tehničnih specifikacij v alinejah}</li>\r\n</ul>"
|
||||
},
|
||||
{
|
||||
"name": "Ovitki in mape",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"model naprave za katero je namenjen\"\", barva v eni besedi\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi tip izdelka lowercase}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Ovitki in torbe za prenosnike",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"dimenzije\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka brez dimenzije in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Pametne ure",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"barva v eni slovenski besedi\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka brez barve in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez barve}</p>\n<H2>{Napiši Novo ime izdelka brez barve in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez barve}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez barve}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Pametne čelade",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"barva v eni slovenski besedi\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka brez barve in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez barve}</p>\n<H2>{Napiši Novo ime izdelka brez barve in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Parni čistilci",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Pečice",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Plastifikatorji",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\r\n\r\nStar_opis_izdelka: {\"OPIS IZDELKA\"};\r\nStaro_ime_izdelka: {\"STARO IME IZDELKA\"};\r\n\r\nUporabi spodnjo GPT predlogo.\r\n\r\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\r\n\r\nGPT predloga:\r\n<name>{Napiši novo ime izdelka po formuli: \"»Plastifikator«\", \"znamka s pravilno kapitalizacijo\", \"model izdelka uppercase\". Ne uporabljaj vejic.}</name>\r\n\r\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\r\n<H2>{Izpostavi en benefit in VKLJUČI Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in NE vsebuje Novo ime izdelka.}</p>\r\n<H2>{Izpostavi en benefit in VKLJUČI Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI Novo ime izdelka.}</p>\r\n<b>Tehnične specifikacije</b>\r\n<ul>\r\n<li>{Napiši 3–10 tehničnih specifikacij v alinejah}</li>\r\n</ul>"
|
||||
},
|
||||
{
|
||||
"name": "Poganjalčki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Pomivalni stroji",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"širina [cm]\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka brez širine in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez širine}</p>\n<H2>{Napiši Novo ime izdelka brez širine in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez širine}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez širine}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Posode za kuhanje",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Postelje",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\r\n\r\nStar_opis_izdelka: {\"OPIS IZDELKA\"};\r\nStaro_ime_izdelka: {\"STARO IME IZDELKA\"};\r\n\r\nUporabi spodnjo GPT predlogo.\r\n\r\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\r\n\r\nGPT predloga:\r\n<name>{Napiši novo ime izdelka po formuli: \"»postelja«\", \"znamka s pravilno kapitalizacijo\", \"model izdelka\", \"velikost v cm (npr. 120x200cm)\". Ne uporabljaj vejic.}</name>\r\n\r\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\r\n<H2>{Napiši material postelje (npr. »Lesena postelja« ali »Kovinska postelja«) in izpostavi en benefit ter VKLJUČI Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI Novo ime izdelka.}</p>\r\n<H2>{Izpostavi en benefit in NE napiši Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI Novo ime izdelka.}</p>\r\n<b>Tehnične specifikacije</b>\r\n<ul>\r\n<li>{Napiši 3–10 tehničnih specifikacij v alinejah}</li>\r\n</ul>"
|
||||
},
|
||||
{
|
||||
"name": "Potrošni material",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\r\n\r\nStar_opis_izdelka: {\"OPIS IZDELKA\"};\r\nStaro_ime_izdelka: {\"STARO IME IZDELKA\"};\r\n\r\nUporabi spodnjo GPT predlogo.\r\n\r\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\r\n\r\nGPT predloga:\r\n<name>{Napiši novo ime izdelka po formuli: \"tip izdelka lowercase\", \"znamka s pravilno kapitalizacijo\", \"model izdelka uppercase\". Ne uporabljaj vejic.}</name>\r\n\r\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\r\n<H2>{Izpostavi en benefit in VKLJUČI Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in NE vsebuje Novo ime izdelka.}</p>\r\n<H2>{Izpostavi en benefit in VKLJUČI Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI Novo ime izdelka.}</p>\r\n<b>Tehnične specifikacije</b>\r\n<ul>\r\n<li>{Napiši 3–10 tehničnih specifikacij v alinejah}</li>\r\n</ul>"
|
||||
},
|
||||
{
|
||||
"name": "Pralni stroji",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"kapaciteta pranja [kg]\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka brez kapacitete pranja in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez kapacitete pranja}</p>\n<H2>{Napiši Novo ime izdelka brez kapacitete pranja in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez kapacitete pranja}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez kapacitete pranja}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Pralno - sušilni stroji",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Prenosne baterije",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"moč polnjenja\"\", \"\"model\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Prenosni računalniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\r\n\r\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\r\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\r\n\r\nUporabi spodnjo GPT predlogo. \r\n\r\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\r\n\r\nGPT predloga:\r\n\r\n\r\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"procesor\"\", \"\"grafična kartica\"\", \"\"pomnilnik\"\", \"\"velikost zaslona v inčih - (primer 13.6'')\"\", \"\"osveževanje (če je 144Hz)\"\", \"\"SD / HD / FHD / UHD / 4K / 8K\"\". Ne uporabljaj vejic.}</name>\r\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\r\n\r\n<H2>{Napiši tip izdelka sentence case in izpostavi en benefit}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\r\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi tip izdelka lowercase}</p>\r\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Projektorji",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Računalniško pohištvo",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Robotski sesalniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Senzorji",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Sesalniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Skiroji in rolke",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"barva v eni slovenski besedi (če je več barvnih variant)\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka brez barve in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka brez barve}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Slušalke",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Sončne celice",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Soundbar zvočniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Stolčki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Strižniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Supi",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Sušilni stroji",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Tablični računalniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"SD / HD / FHD / UHD / 4K / 8K\"\", \"\"velikost pomnilnika\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši tip izdelka sentence case in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi tip izdelka lowercase}</p>\n<H2>{Napiši naslov, ki izpostavlja en benefit in VKLJUČI tudi tip izdelka lowercase.}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Televizorji",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"SD / HD / FHD / UHD / 4K / 8K\"\", \"\"TV\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\", \"\"diagonala v inčih in označeno z '.\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka brez diagonale in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka brez diagonale in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Tipkovnice",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Tiskalniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Tiskalniki nalepk in trakov",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\r\n\r\nStar_opis_izdelka: {\"OPIS IZDELKA\"};\r\nStaro_ime_izdelka: {\"STARO IME IZDELKA\"};\r\n\r\nUporabi spodnjo GPT predlogo.\r\n\r\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\r\n\r\nGPT predloga:\r\n<name>{Napiši novo ime izdelka po formuli: \"»Tiskalnik nalepk«\", \"znamka s pravilno kapitalizacijo\", \"model izdelka uppercase\". Ne uporabljaj vejic.}</name>\r\n\r\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\r\n<H2>{Izpostavi en benefit in VKLJUČI Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in NE vsebuje Novo ime izdelka.}</p>\r\n<H2>{Izpostavi en benefit in VKLJUČI Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI Novo ime izdelka.}</p>\r\n<b>Tehnične specifikacije</b>\r\n<ul>\r\n<li>{Napiši 3–10 tehničnih specifikacij v alinejah}</li>\r\n</ul>"
|
||||
},
|
||||
{
|
||||
"name": "Tobogani in gugalnice",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Trampolini",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic. Ne vključuj barve v ime.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Uničevalci dokumentov",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\r\n\r\nStar_opis_izdelka: {\"OPIS IZDELKA\"};\r\nStaro_ime_izdelka: {\"STARO IME IZDELKA\"};\r\n\r\nUporabi spodnjo GPT predlogo.\r\n\r\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\r\n\r\nGPT predloga:\r\n<name>{Napiši novo ime izdelka po formuli: \"»Uničevalec dokumentov«\", \"znamka s pravilno kapitalizacijo\", \"model izdelka uppercase\". Ne uporabljaj vejic.}</name>\r\n\r\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\r\n<H2>{Izpostavi en benefit in VKLJUČI Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in NE vsebuje Novo ime izdelka.}</p>\r\n<H2>{Izpostavi en benefit in VKLJUČI Novo ime izdelka}</H2>\r\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI Novo ime izdelka.}</p>\r\n<b>Tehnične specifikacije</b>\r\n<ul>\r\n<li>{Napiši 3–10 tehničnih specifikacij v alinejah}</li>\r\n</ul>"
|
||||
},
|
||||
{
|
||||
"name": "Visokotlačni čistilci",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Vlažilniki in čistilci zraka",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Vozički",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Vrtno orodje",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Vtičnice",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"vrsta vtičnice (npr. RJ45)\"\", \"\"tip izdelka lowercase\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Zamrzovalniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Zaščitna stekla",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka izdelka\"\", \"\"znamka in model naprave, za katere je namenjen\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Zimski športi",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Zobne prhe",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Čistilci oken in tal",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Športi z žogo",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Štedilniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Žari",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"tip izdelka sentence case\"\", \"\"znamka s pravilno kapitalizacijo\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
},
|
||||
{
|
||||
"name": "Žepni bralniki",
|
||||
"prompt": "Ustvari nov opis izdelka v Slovenščini z naslednjimi spremenljivkami:\n\nStar_opis_izdelka: {\"\"OPIS IZDELKA\"\"};\nStaro_ime_izdelka: {\"\"STARO IME IZDELKA\"\"};\n\nUporabi spodnjo GPT predlogo. \n\nSledi tej GPT predlogi stavek po stavek in sestavi nov opis izdelka:\n\nGPT predloga:\n\n\n<name>{Napiši novo ime izdelka po formuli: \"\"znamka s pravilno kapitalizacijo\"\", \"\"tip izdelka lowercase\"\", \"\"\"poln model izdelka, če lahko z besedo in ID uppercase\"\"\". Ne uporabljaj vejic.}</name>\n<metaDescription>{Najprej napiši Novo_ime_izdelka in potem nadaljuj dokler nisi zapisal 140 znakov vključno s presledki}</metaDescription>\n\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p>\n<H2>{Izpostavi en benefit, in NE napiši Novo ime izdelka}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed in VKLJUČI tudi Novo ime izdelka.}</p>\n<H2>{Napiši Novo ime izdelka in izpostavi en benefit}</H2>\n<p>{Napiši odstavek, ki je dolg 100 besed, ki NE vsebuje Novo ime izdelka.}</p><b>{Tehnične specifikacije}</b><ul><li>{Napiši 3-10 tehničnih specifikacij v alinejah}</li></ul>"
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,170 @@
|
||||
{
|
||||
"version": 1,
|
||||
"articles": [
|
||||
{
|
||||
"slug": "tech-architecture-overview",
|
||||
"title": "Architecture overview (Go API + SvelteKit)",
|
||||
"body_md": "# Architecture overview\n\nDescrybe v2 is a **Go chi API + SvelteKit web + PostgreSQL** rewrite (no Clerk).\n\n## Runtime processes\n\n| Process | Role |\n|---------|------|\n| apps/api/cmd/api | HTTP API (sessions, CSRF, public /api/v1, admin) |\n| apps/api/cmd/worker | Processing jobs, Woo/Shopify claim, support AI auto jobs, billing cycles |\n| apps/web | SvelteKit 2 / Svelte 5 UI (Vite proxies /api to API) |\n| PostgreSQL 16 | System of record (goose migrations under apps/api/sql/schema) |\n\n`npm run dev` starts API + web. **Worker is separate** — without it, process jobs and many background syncs stall.\n\n## Component diagram\n\n`mermaid\nflowchart LR\n Browser[\"Browser :28472\"] --> Web[\"SvelteKit apps/web\"]\n Web -->|\"/api proxy\"| API[\"Go chi API :28471\"]\n API --> PG[(PostgreSQL)]\n Worker[\"cmd/worker\"] --> PG\n API -->|\"NOTIFY processing_jobs\"| Worker\n Worker -->|\"ClaimNext SKIP LOCKED\"| PG\n Ext[\"OpenAI / Woo / Shopify / Stripe / SMTP\"] -.-> API\n Ext -.-> Worker\n`\n\n## Auth surfaces\n\n- **Dashboard session:** cookie + CSRF (X-CSRF-Token) under /api/*\n- **Public API key:** Bearer or X-API-Key under /api/v1 (no CSRF)\n- **Public tokens:** /api/public/* (export feeds CSV/XML, unsubscribe, brand logos)\n- **Platform admin:** /api/admin/* after RequirePlatformAdmin (support desk subset for support_staff)\n\n## Key packages\n\n- internal/httpapi — routes + middleware\n- internal/processing — product description jobs\n- internal/feeds / woocommerce / shopify — ingest and connectors\n- internal/support — tickets, KB, FAQ/AI auto-reply\n- internal/billing — plans, credits, Stripe\n- internal/jobs — enqueue processing (Postgres pending + NOTIFY; River client deferred)\n\nSources: README.md, apps/api/internal/httpapi/server.go, apps/api/cmd/worker/main.go.",
|
||||
"category_slugs": [
|
||||
"architecture",
|
||||
"operations",
|
||||
"admin"
|
||||
],
|
||||
"keywords": [
|
||||
"architecture",
|
||||
"stack",
|
||||
"worker",
|
||||
"sveltekit",
|
||||
"chi",
|
||||
"postgres",
|
||||
"overview",
|
||||
"diagram"
|
||||
],
|
||||
"intent_keys": [
|
||||
"how_is_descrybe_built",
|
||||
"what_processes_run",
|
||||
"architecture_overview"
|
||||
],
|
||||
"is_published": true,
|
||||
"priority_weight": 100
|
||||
},
|
||||
{
|
||||
"slug": "tech-api-v1-postman-a1",
|
||||
"title": "Public API /api/v1 and Postman A1 flows",
|
||||
"body_md": "# Public API and Postman A1\n\nBase URL (local): http://127.0.0.1:28471 \nAuth: Authorization Bearer api_key or X-API-Key \nOpenAPI: GET /api/v1/openapi.yaml (no key). Health: GET /api/v1/health or GET /healthz.\n\n## Core /api/v1 groups (API key)\n\n| Group | Examples |\n|-------|----------|\n| Products | GET /products, GET /products/{id}, PATCH /products/{id}, POST /products/process, GET /products/process/{id} |\n| Feeds | GET/POST /feeds, POST /feeds/{id}/sync, mappings, extract-schema |\n| Categories / attributes | CRUD under /categories, /attributes |\n| Export | /export-feeds plus generate / export-products |\n| Process jobs | POST /process, list/get/cancel/retry |\n| Marketing calendar | /marketing/calendar (legacy /campaigns aliases) |\n\nMounted in apps/api/internal/httpapi/v1.go via mountV1.\n\n## A1 two-EAN Postman flow\n\nCollection: docs/postman/Descrybe-v2-A1-two-EANs.postman_collection.json \nPrerequisite: npm run seed:a1 (restores processing jobs so poll works).\n\n`mermaid\nsequenceDiagram\n participant P as Postman\n participant API as /api/v1\n participant W as worker\n P->>API: GET /health\n P->>API: GET /feeds\n P->>API: GET /products?feed_id=\n P->>API: POST /products/process (2 EANs)\n API-->>P: data.process_id\n loop until done\n P->>API: GET /products/process/{processId}\n end\n Note over W: Worker claims processing_jobs\n P->>API: GET /products?search=EAN\n P->>API: GET /export-feeds\n P->>API: GET /api/public/export-feeds/{token}.csv\n`\n\n### Steps (collection order)\n\n0. Health (no auth) \n1. List feeds (find Elkotex) \n2. List products on feed \n3. **Process two EANs** — processing_type full — copy data.process_id into processId \n4. Poll process status \n5–6. Search results by EAN \n7. Get product by UUID \n8. List export feeds \n9. Public CSV (no API key)\n\nDemo API key and feed/EAN vars live in the Postman collection (local demo only). Full surface: docs/postman/Descrybe-v2-Demo-A1-all-v1.postman_collection.json.\n\nRate limits: process/sync/export POSTs are capped per company (RateLimitV1Process). Prefer enqueue + worker under load.",
|
||||
"category_slugs": [
|
||||
"api",
|
||||
"processing",
|
||||
"integrations"
|
||||
],
|
||||
"keywords": [
|
||||
"api",
|
||||
"postman",
|
||||
"ean",
|
||||
"process",
|
||||
"openapi",
|
||||
"v1",
|
||||
"bearer",
|
||||
"export",
|
||||
"feed"
|
||||
],
|
||||
"intent_keys": [
|
||||
"how_to_use_api",
|
||||
"postman_a1",
|
||||
"process_eans_api"
|
||||
],
|
||||
"is_published": true,
|
||||
"priority_weight": 95
|
||||
},
|
||||
{
|
||||
"slug": "tech-admin-capabilities-diagnostics",
|
||||
"title": "Admin capabilities and diagnosing issues",
|
||||
"body_md": "# Admin capabilities and diagnostics\n\nPlatform admins use /admin/* (session + RequirePlatformAdmin). Support desk staff get ticket routes only (RequireSupportDesk).\n\n## Useful admin APIs\n\n| Path | Purpose |\n|------|---------|\n| GET /api/admin/diagnostics | Health: DB, queue, cache, storage, mail; config sanity (booleans only); recent job/AI failures |\n| GET /api/admin/analytics | Operational metrics dashboard data |\n| GET /api/admin/readiness | Cutover hypercare: must_set_password, companies without admin/plan |\n| GET /api/admin/jobs | Recent processing_jobs |\n| POST /api/admin/jobs/stuck-cleanup | Stuck running jobs cleanup |\n| GET/PUT /api/admin/settings | Platform settings (secrets masked on GET) |\n| GET/POST /api/admin/support/kb/articles | Support Knowledge CRUD |\n| GET/PUT /api/admin/support/auto-config | FAQ + AI auto-reply switchboard |\n| Users / companies / plans / credits | Org + billing admin |\n\nUI: /admin/support/knowledge; diagnostics and analytics under the admin shell.\n\n## Diagnose a stuck process\n\n`mermaid\nflowchart TD\n A[Job stuck or failed] --> B{Worker running?}\n B -->|No| C[\"Start: go run ./cmd/worker\"]\n B -->|Yes| D[\"GET /api/admin/diagnostics\"]\n D --> E{queue.failed or stuck_running?}\n E -->|Yes| F[\"GET /api/admin/jobs + stuck-cleanup\"]\n E -->|No| G[\"Check /readyz + company credits\"]\n F --> H[\"Retry job or re-POST /products/process\"]\n`\n\n### Checklist\n\n1. GET /healthz (liveness) and GET /readyz (Postgres + maintenance/read_only flags). \n2. GET /api/admin/diagnostics — overall ok|degraded|fail; never expect secrets in the payload. \n3. Confirm **worker** process is up (API alone does not drain processing). \n4. Filter recent failures: ?status=failed&failures_limit=25. \n5. Support auto AI: ticket auto_reply_status, /admin/support inbox flag=needs_human, AI role support under /admin/settings. \n6. Cutover: GET /api/admin/readiness before DNS switch.\n\nDiagnostics intentionally excludes marketing charts — use analytics for trends, diagnostics for troubleshooting.\n\nSources: apps/api/internal/httpapi/admin_diagnostics_handlers.go, server.go admin routes.",
|
||||
"category_slugs": [
|
||||
"admin",
|
||||
"operations",
|
||||
"bug"
|
||||
],
|
||||
"keywords": [
|
||||
"admin",
|
||||
"diagnostics",
|
||||
"readiness",
|
||||
"stuck",
|
||||
"jobs",
|
||||
"healthz",
|
||||
"readyz",
|
||||
"troubleshoot"
|
||||
],
|
||||
"intent_keys": [
|
||||
"how_to_diagnose",
|
||||
"admin_diagnostics",
|
||||
"stuck_job"
|
||||
],
|
||||
"is_published": true,
|
||||
"priority_weight": 90
|
||||
},
|
||||
{
|
||||
"slug": "tech-jobs-queues-integrations",
|
||||
"title": "Jobs, queues, and integrations",
|
||||
"body_md": "# Jobs, queues, and integrations\n\n## Processing queue\n\nASSUMPTION in code: full River client is deferred. Production MVP uses **Postgres processing_jobs** with FOR UPDATE SKIP LOCKED + pg_notify('processing_jobs').\n\n- Enqueue: internal/jobs.Queue.EnqueueProcessingJob\n- Workers: internal/processing.JobSlots.Fill → ClaimNext (count from config / ClampProcessingWorkers)\n- Process starts also hit HTTP rate limits (RPM per company)\n\n## Worker loop (what runs)\n\nFrom apps/api/cmd/worker:\n\n1. Fill processing job slots \n2. ProcessPendingAutoJobs (support AI fallback) \n3. WooCommerce / Shopify ClaimNextPendingJob + sync \n4. Periodic: EnqueueDueScheduled (stores), RunDueBillingCycles\n\nAPI also runs a light RunAutoJobsLoop for support AI — keep **worker** in production.\n\n`mermaid\nflowchart TB\n subgraph ingest [Ingest]\n FeedURL[Feed URL / CSV]\n Woo[WooCommerce]\n Shop[Shopify]\n end\n subgraph core [Core]\n Jobs[(processing_jobs)]\n Worker[cmd/worker]\n Catalog[(products)]\n end\n subgraph out [Outbound]\n Export[export feeds CSV/XML]\n StorePush[Woo/Shopify push]\n end\n FeedURL --> Catalog\n Woo --> Catalog\n Shop --> Catalog\n Catalog --> Jobs\n Jobs --> Worker\n Worker --> Catalog\n Catalog --> Export\n Worker --> StorePush\n`\n\n## Integrations (where configured)\n\n| Integration | Preferred config | Notes |\n|-------------|------------------|-------|\n| AI (BYOK / OpenAI-compatible) | Tenant /integrations/ai | Encrypted with APP_ENCRYPTION_KEY; optional process OPENAI_* fallback |\n| Marketing email | /integrations/email | Separate from platform invite SMTP |\n| Woo / Shopify / feeds | /stores | Woo secrets at rest; Shopify Admin domain SSRF-hardened |\n| Stripe / EPREL / feed private-URL allowlist | /admin/settings | Env fallbacks exist; prefer UI |\n| Platform invite SMTP | Process SMTP_* | See docs/ops-runtime.md |\n\nSupport AI auto-reply jobs: table support_auto_jobs → TryAutoReplyLLM after FAQ miss.\n\nSources: apps/api/internal/jobs/river.go, apps/api/cmd/worker/main.go, docs/ops-runtime.md.",
|
||||
"category_slugs": [
|
||||
"operations",
|
||||
"integrations",
|
||||
"processing"
|
||||
],
|
||||
"keywords": [
|
||||
"queue",
|
||||
"worker",
|
||||
"jobs",
|
||||
"woocommerce",
|
||||
"shopify",
|
||||
"billing",
|
||||
"river",
|
||||
"sync",
|
||||
"integration"
|
||||
],
|
||||
"intent_keys": [
|
||||
"how_queues_work",
|
||||
"worker_required",
|
||||
"store_sync"
|
||||
],
|
||||
"is_published": true,
|
||||
"priority_weight": 88
|
||||
},
|
||||
{
|
||||
"slug": "tech-configuration-env",
|
||||
"title": "Configuration and bootstrap environment",
|
||||
"body_md": "# Configuration and bootstrap environment\n\n**One root .env** — copy from .env.example. Do **not** create apps/api/.env. Product secrets belong in the dashboard after login.\n\n## Required bootstrap (names only — never paste real secrets)\n\n| Variable | Purpose |\n|----------|---------|\n| DATABASE_URL | Postgres (local compose often host port 5433) |\n| APP_ENV | development / staging / production |\n| HTTP_ADDR | API listen (dev commonly :28471) |\n| WEB_ORIGIN | Browser origin for CORS/cookies (:28472 local) |\n| PUBLIC_API_URL | Public API origin for the web app |\n| SESSION_SECURE | Cookie Secure; must be true in production |\n| TOKEN_SIGNING_SECRET | Session/invite HMAC (openssl rand -hex 32) |\n| APP_ENCRYPTION_KEY | At-rest encryption for BYOK/store secrets (preferred) |\n\n## Optional bootstrap (safe to override)\n\nTRUSTED_PROXIES, SESSION_COOKIE_NAME, CSRF_COOKIE_NAME, PUBLIC_CSRF_COOKIE_NAME, SESSION_IDLE_HOURS, UPLOAD_DIR, MAINTENANCE_MODE, READ_ONLY_MODE, CREDENTIALS_ENCRYPTION_KEY (legacy alias for APP_ENCRYPTION_KEY), DOTENV_PATH.\n\n## Prefer dashboard (not root .env)\n\n| Area | UI |\n|------|-----|\n| Stripe, EPREL kill-switch, feed private-URL allowlist | /admin/settings |\n| Tenant AI | /integrations/ai |\n| Marketing email | /integrations/email |\n| Stores | /stores |\n\nOptional process-env fallbacks still accepted by some resolvers (OPENAI_*, SMTP_*, EPREL_*, FEED_URL_PRIVATE_ALLOWLIST) — prefer UI for day-to-day.\n\nProduction fail-closed (APP_ENV=production): SESSION_SECURE=true, https WEB_ORIGIN, APP_ENCRYPTION_KEY, TOKEN_SIGNING_SECRET, STRIPE_MOCK=false.\n\nNever commit real secrets. Diagnostics exposes **presence flags** only (*_set), never values.\n\nSource: root .env.example, README.md Environment section, docs/ops-runtime.md.",
|
||||
"category_slugs": [
|
||||
"configuration",
|
||||
"operations",
|
||||
"security"
|
||||
],
|
||||
"keywords": [
|
||||
"env",
|
||||
"configuration",
|
||||
"DATABASE_URL",
|
||||
"TOKEN_SIGNING_SECRET",
|
||||
"APP_ENCRYPTION_KEY",
|
||||
"WEB_ORIGIN",
|
||||
"bootstrap"
|
||||
],
|
||||
"intent_keys": [
|
||||
"how_to_configure",
|
||||
"env_vars",
|
||||
"production_settings"
|
||||
],
|
||||
"is_published": true,
|
||||
"priority_weight": 85
|
||||
},
|
||||
{
|
||||
"slug": "tech-security-ops-runbook",
|
||||
"title": "Security and operational runbook",
|
||||
"body_md": "# Security and operational runbook\n\nGrounded in docs/security-notes.md and docs/ops-runtime.md.\n\n## Controls in place\n\n| Area | Control |\n|------|---------|\n| CSRF | Double-submit cookie + X-CSRF-Token on dashboard /api/* (skipped for /api/v1, /api/public/*, webhooks) |\n| Sessions | scs + Postgres store; HttpOnly; idle SESSION_IDLE_HOURS (default 24); absolute 7d |\n| CORS | Allowlist = WEB_ORIGIN only; credentials allowed |\n| SSRF | Feed + Woo URL checks; Shopify *.myshopify.com; optional FEED_URL_PRIVATE_ALLOWLIST / settings allowlist |\n| Uploads | CSV/logo size + type caps under UPLOAD_DIR/{company_id}/ |\n| AuthZ | Session company context; API key company binding; admin vs support_staff |\n| Rate limits | Auth POSTs / IP; process/sync/export / company (in-process — not cluster-global) |\n\n## Ops runbook\n\n`mermaid\nflowchart LR\n Deploy --> Migrate[\"scripts/migrate.ps1 / goose up\"]\n Migrate --> API[cmd/api]\n Migrate --> Worker[cmd/worker]\n API --> Probes[\"/healthz /readyz\"]\n Worker --> Probes\n Probes --> Hypercare[\"/api/admin/readiness + diagnostics\"]\n`\n\n1. **Bring up:** Docker Postgres → migrate → API + **worker** → web. \n2. **Probes:** /healthz no DB; /readyz pings Postgres and reports maintenance/read_only. \n3. **Maintenance:** MAINTENANCE_MODE / READ_ONLY_MODE — keep probes green during cutover rehearsal. \n4. **Mail:** Platform invites need SMTP_ENABLED + host/from; tenant marketing mail is separate. \n5. **Credentials:** Set APP_ENCRYPTION_KEY before storing production Woo/AI secrets; rotating without re-save breaks ciphertext. \n6. **Stuck jobs:** diagnostics → stuck-cleanup → retry; ensure worker is running. \n7. **Support auto-reply:** default off (enabled=false); publish KB + raise threshold before enabling FAQ; AI needs support role configured. \n8. **Never log:** Stripe/OpenAI/SMTP/Woo/Shopify/EPREL secrets.\n\n## Known residual risks (honest)\n\nIn-process rate limits do not cluster; broad private feed allowlists re-enable SSRF; public export tokens rely on entropy; demo API keys are local-only.\n\nFor cutover blockers and SMTP verification, see docs/ops-runtime.md and docs/production-checklist.md.",
|
||||
"category_slugs": [
|
||||
"security",
|
||||
"operations",
|
||||
"admin"
|
||||
],
|
||||
"keywords": [
|
||||
"security",
|
||||
"csrf",
|
||||
"ssrf",
|
||||
"runbook",
|
||||
"maintenance",
|
||||
"encryption",
|
||||
"rate limit",
|
||||
"cutover"
|
||||
],
|
||||
"intent_keys": [
|
||||
"security_checklist",
|
||||
"ops_runbook",
|
||||
"production_hardening"
|
||||
],
|
||||
"is_published": true,
|
||||
"priority_weight": 92
|
||||
}
|
||||
],
|
||||
"scope": "tech-ops-admin",
|
||||
"agent": 7
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
{
|
||||
"version": 1,
|
||||
"source": "agent-6-product-user-facing",
|
||||
"notes": "Bodies use public static assets from apps/web/static (served at /…). Content grounded in README, site marketing data, getting-started, free-tier, store-connectors, eprel, processing API, and plan catalog.",
|
||||
"articles": [
|
||||
{
|
||||
"slug": "what-is-descrybe",
|
||||
"title": "What is Descrybe?",
|
||||
"category_slugs": ["getting-started", "product"],
|
||||
"keywords": [
|
||||
"what is descrybe",
|
||||
"descrybe",
|
||||
"product catalog",
|
||||
"supplier feeds",
|
||||
"pim",
|
||||
"overview",
|
||||
"about"
|
||||
],
|
||||
"intent_keys": ["what_is_descrybe", "product_overview", "about_platform"],
|
||||
"is_published": true,
|
||||
"priority_weight": 100,
|
||||
"body_md": "# What is Descrybe?\n\nDescrybe turns **supplier CSV and XML feeds** into clean, channel-ready product catalogs. You map fields to your categories and attributes, enrich listing copy, then ship data through **export feeds**, **WooCommerce sync**, or the **public API**.\n\n\n\n## What Descrybe does\n\n1. **Import** — Connect a supplier feed URL or upload a CSV.\n2. **Map** — Match source columns/paths to your standard fields.\n3. **Enrich** — Categorize, fill attributes, and (on paid plans) generate AI titles and descriptions.\n4. **Ship** — Generate Google Shopping / Meta / custom CSV|XML export feeds, sync WooCommerce, or pull via `/api/v1`.\n\n\n\n## Who it is for\n\nEcommerce teams that receive messy multi-supplier data and need consistent categories, attributes, and listing copy before products go live on storefronts or shopping channels.\n\n## What it is not\n\n- Not a general website builder.\n- Not a 500-channel marketplace connector (core paths are feeds, WooCommerce, Shopify guidance, and export poll URLs).\n- FTP/FTPS feed sync is **not** supported in the current product UI.\n\n## Next steps\n\n- See knowledge article **Getting started: feed → process → export** (`getting-started-feed-process-export`).\n- Or start the in-app tutorial from **Dashboard → Start tutorial** after you sign in.\n"
|
||||
},
|
||||
{
|
||||
"slug": "getting-started-feed-process-export",
|
||||
"title": "Getting started: feed → process → export",
|
||||
"category_slugs": ["getting-started", "howto"],
|
||||
"keywords": [
|
||||
"getting started",
|
||||
"how to start",
|
||||
"first steps",
|
||||
"onboarding",
|
||||
"tutorial",
|
||||
"workflow",
|
||||
"feed process export"
|
||||
],
|
||||
"intent_keys": ["getting_started", "first_run", "core_workflow"],
|
||||
"is_published": true,
|
||||
"priority_weight": 95,
|
||||
"body_md": "# Getting started: feed → process → export\n\nThis is the main operator path in Descrybe.\n\n## 1. Sign in\n\nOpen **Login**, then land on **Dashboard**. Confirm your active company in the header switcher.\n\n## 2. Enable standard fields\n\nGo to **Standard Fields** (More → Setup) and enable the **recommended** fields. These become the dictionary your feed mappings target.\n\n## 3. Add a supplier feed\n\n- **Stores** hub → Feed URL / CSV cards, or\n- **Feeds** → add with `source=url` (public http(s) URL) or `source=file` (CSV upload).\n\nFTP/FTPS is not supported yet.\n\n\n\n## 4. Map the feed\n\nOpen the feed → **Map**:\n\n1. Select the item element (XML) or confirm CSV columns.\n2. Run **Auto-map**, then review bindings.\n3. **Save mappings**.\n4. Use **Sync + Process sample** (or Sync, then process from Products).\n\n## 5. Process products\n\nKeep the **background worker** running. On **Products**, use tabs such as Unprocessed / Processed / Needs review / Error / Processing. Select rows and run processing, or watch jobs under **Background Tasks** (`/processing`).\n\n\n\n## 6. Export\n\nOpen **Export feeds**, create or open a template (Google Shopping CSV/XML, Meta CSV, or custom), **Generate / Refresh**, then copy the **public poll URL** into Merchant Center or your partner importer.\n\n\n\n## Tip\n\nUse **Start tutorial** on the dashboard for a guided click-through of the same path (standard fields → feeds → map → products → export → campaigns).\n"
|
||||
},
|
||||
{
|
||||
"slug": "how-to-import-supplier-feeds",
|
||||
"title": "How to import supplier feeds (URL and CSV)",
|
||||
"category_slugs": ["feeds", "howto"],
|
||||
"keywords": [
|
||||
"import feed",
|
||||
"supplier feed",
|
||||
"csv upload",
|
||||
"xml feed",
|
||||
"feed url",
|
||||
"add feed",
|
||||
"sync feed"
|
||||
],
|
||||
"intent_keys": ["import_feeds", "add_feed", "csv_upload"],
|
||||
"is_published": true,
|
||||
"priority_weight": 80,
|
||||
"body_md": "# How to import supplier feeds\n\nDescrybe ingests supplier catalogs as **input feeds**, then stores rows as raw products for mapping and processing.\n\n## Feed URL\n\n1. Go to **Feeds** or **Stores → Feed URL**.\n2. Create a feed with a public **http(s)** CSV or XML URL.\n3. Save, open **Map**, then **Sync** (or Sync + Process sample).\n\nScheduled pull loads data into `raw_products` for your company.\n\n## CSV upload\n\n1. Choose **CSV file** when adding a feed.\n2. Upload the file (stored as a file-backed feed source).\n3. Map columns and sync.\n\n## Supported today\n\n| Source | Status |\n|--------|--------|\n| Public Feed URL (CSV/XML) | Ready |\n| CSV upload | Ready |\n| WooCommerce REST | Ready (Stores → WooCommerce) |\n| Shopify Admin API | Ready for config/test/push (see Shopify article) |\n| FTP / FTPS | **Not supported** in UI yet |\n\n## After import\n\nAlways map fields before expecting clean processed products. Unmapped syncs still create raw rows you can process later from **Products → Unprocessed**.\n"
|
||||
},
|
||||
{
|
||||
"slug": "how-to-map-feed-fields",
|
||||
"title": "How to map feed fields to standard fields",
|
||||
"category_slugs": ["feeds", "howto"],
|
||||
"keywords": [
|
||||
"mapping",
|
||||
"auto-map",
|
||||
"standard fields",
|
||||
"feed mapping",
|
||||
"field mapping",
|
||||
"columns"
|
||||
],
|
||||
"intent_keys": ["feed_mapping", "auto_map", "standard_fields"],
|
||||
"is_published": true,
|
||||
"priority_weight": 78,
|
||||
"body_md": "# How to map feed fields\n\nMapping tells Descrybe how supplier columns/paths become your product fields.\n\n## Before you map\n\n1. Open **Standard Fields** and enable recommended fields (and any custom groups you need).\n2. Optionally set up **Categories** and **Attributes** so enrichment has a taxonomy to target.\n\n\n\n## Mapping steps\n\n1. Open **Feeds** → choose a feed → **Map** (`/feeds/{id}/mapping`).\n2. Select the repeating **item** element for XML feeds (or confirm CSV header row).\n3. Click **Auto-map** to suggest bindings to product + standard fields.\n4. Adjust transforms / paths as needed.\n5. **Save mappings**.\n6. **Sync** or **Sync + Process sample**.\n\n## Tips\n\n- Auto-map is a starting point — verify GTIN/EAN, title, price, and image fields carefully.\n- Company setting **merge-by-GTIN** (when enabled) affects how duplicate barcodes are merged across feeds.\n- If **Map** is missing from the tutorial, create a feed first; the tour does not invent demo feeds for you.\n"
|
||||
},
|
||||
{
|
||||
"slug": "process-products-and-ean-api",
|
||||
"title": "Process products and EAN / barcode API flow",
|
||||
"category_slugs": ["processing", "api", "howto"],
|
||||
"keywords": [
|
||||
"process products",
|
||||
"ean",
|
||||
"barcode",
|
||||
"gtin",
|
||||
"api process",
|
||||
"processing job",
|
||||
"products/process",
|
||||
"background tasks"
|
||||
],
|
||||
"intent_keys": ["process_products", "ean_flow", "api_process"],
|
||||
"is_published": true,
|
||||
"priority_weight": 90,
|
||||
"body_md": "# Process products and EAN / API flow\n\nProcessing turns **raw** supplier rows into **processed** catalog products (category, attributes, titles, descriptions, optional EPREL / AI).\n\n## In the dashboard\n\n1. Ensure the **worker** process is running (`go run ./cmd/worker` in local setups).\n2. Open **Products**.\n3. Select unprocessed / raw items (or use Sync + Process sample on a feed mapping).\n4. Choose processing actions (categories, attributes, AI titles/descriptions when your plan allows).\n5. Monitor jobs under **Background Tasks** (`/processing`). Cancel/retry from the job detail when available.\n\n## Public API (EAN items)\n\nAuthenticate with an API key: `Authorization: Bearer dk_…` (create keys under **Settings → API keys**).\n\nLegacy-compatible start:\n\n- `POST /api/v1/products/process`\n- Alias: `POST /api/v1/process`\n\nEvery item in `items` **must include a non-empty `ean` field**. Missing EAN returns a validation error.\n\n`processing_type` may be:\n\n- `\"full\"` (default)\n- a single step: `category`, `title`, `description`, `attributes`\n- an array of steps, e.g. `[\"title\",\"attributes\"]`\n\nPoll status with `GET /api/v1/process/{id}` (or the products/process variant).\n\n## Credits and plan gates\n\n- **Normalize / parse specs / fill fields** can run on Free without AI credits.\n- **AI titles & descriptions** need `can_use_ai` (paid plan or remaining AI credits). Free has **0** monthly AI credits.\n- Hitting the SKU cap returns a product-limit error until you free capacity or upgrade.\n\n## Local smoke tip\n\nThe A1 demo Postman collection processes real EANs against a seeded feed after `npm run seed:a1`. Use your own EANs from your mapped feeds in production.\n"
|
||||
},
|
||||
{
|
||||
"slug": "ai-titles-and-descriptions",
|
||||
"title": "AI titles and product descriptions",
|
||||
"category_slugs": ["catalog", "ai", "howto"],
|
||||
"keywords": [
|
||||
"ai description",
|
||||
"ai title",
|
||||
"generate description",
|
||||
"listing copy",
|
||||
"meta title",
|
||||
"meta description",
|
||||
"enhance",
|
||||
"openai"
|
||||
],
|
||||
"intent_keys": ["ai_descriptions", "ai_titles", "listing_copy"],
|
||||
"is_published": true,
|
||||
"priority_weight": 85,
|
||||
"body_md": "# AI titles and product descriptions\n\nDescrybe can generate search-friendly titles, descriptions, and meta snippets during processing.\n\n\n\n## How to run AI enrichment\n\n1. Configure AI under **Integrations → AI** (tenant BYOK) and/or rely on platform AI settings when your plan uses managed credits.\n2. On **Products**, choose AI title / description processing actions (Starter+ or any plan with AI credits).\n3. Review results on the product — tabs such as **Needs review** help you accept, edit, or reject enrichment.\n\nCategory **title formulas** and **description formulas** (Categories → formula editors) shape structured output alongside AI.\n\n## Free vs paid\n\n| Capability | Free | Starter+ |\n|------------|------|----------|\n| Map feeds, clean data, fill attributes | Yes | Yes |\n| AI titles & descriptions | No (0 AI credits; `can_use_ai` false) | Yes (monthly credits) |\n| Bring your own AI key | No | Growth+ packaging / feature flags |\n\nIf AI is skipped, processing notes may say the enhance step was skipped for Free plan — that is expected, not a worker crash.\n\n## Brand voice\n\nSet tone and preferred terms under **Brand kit** (`/brand`). When AI is allowed, brand voice can be injected into prompts (`capability.brand_ai_apply`).\n"
|
||||
},
|
||||
{
|
||||
"slug": "export-feeds-channels",
|
||||
"title": "Export feeds to shopping channels",
|
||||
"category_slugs": ["export", "howto"],
|
||||
"keywords": [
|
||||
"export feed",
|
||||
"google shopping",
|
||||
"meta catalog",
|
||||
"merchant center",
|
||||
"csv export",
|
||||
"xml export",
|
||||
"public url"
|
||||
],
|
||||
"intent_keys": ["export_feeds", "google_shopping", "channel_export"],
|
||||
"is_published": true,
|
||||
"priority_weight": 82,
|
||||
"body_md": "# Export feeds to shopping channels\n\nOutbound feeds publish your processed catalog for partners and ad platforms.\n\n\n\n## Create and generate\n\n1. Open **Export feeds**.\n2. Create a template — presets include **Google Shopping** (CSV/XML) and **Meta** CSV, plus custom CSV/XML.\n3. Map export fields to Descrybe sources (titles, attributes, EPREL keys, images, price, …).\n4. Click **Generate / Refresh**.\n5. Copy the **public** URL:\n - `GET /api/public/export-feeds/{token}.csv`\n - `GET /api/public/export-feeds/{token}.xml`\n\nNo login is required to poll that URL (treat the token as a secret).\n\n## Channel notes\n\n- **Google Merchant Center** — schedule fetch against the public URL after Refresh.\n- **Meta Commerce Manager** — use the Meta CSV preset fields (id, title, availability, price, image_link, …).\n- **Custom / affiliates** — define your own keys and XML root/item names.\n\nDescrybe does **not** push arbitrary outbound product webhooks yet; partners should poll the public URL or use the REST API.\n\n## From Products\n\nYou can also export a **selected** product set from the Products page into an export template when that action is available on your plan.\n"
|
||||
},
|
||||
{
|
||||
"slug": "woocommerce-and-shopify",
|
||||
"title": "Connect WooCommerce and Shopify",
|
||||
"category_slugs": ["stores", "howto"],
|
||||
"keywords": [
|
||||
"woocommerce",
|
||||
"shopify",
|
||||
"store connector",
|
||||
"sync products",
|
||||
"stores hub",
|
||||
"orders",
|
||||
"reviews"
|
||||
],
|
||||
"intent_keys": ["woocommerce", "shopify", "store_connectors"],
|
||||
"is_published": true,
|
||||
"priority_weight": 75,
|
||||
"body_md": "# Connect WooCommerce and Shopify\n\nUse the **Stores** hub (`/stores`) as the entry point for store platforms and file sources.\n\n## WooCommerce\n\n1. Open **Stores → WooCommerce** (`/woocommerce`).\n2. Enter store URL + REST credentials (stored encrypted).\n3. **Test connection**, then queue product sync.\n4. Sync **Orders** and **Reviews** from the Woo tabs when you need campaign audiences or review content.\n\nReviews also appear under Marketing → Reviews (redirects to the Woo reviews tab).\n\n## Shopify\n\n1. Open **Stores → Shopify** (`/stores/shopify`).\n2. Configure shop domain + Admin API token (SSRF-safe domain checks apply).\n3. Test connection; product push and orders pull are available (dry-run proven in local QA).\n\nNative Shopify product reviews are **not** supported (no first-party Admin API for that).\n\n## Feed URL / CSV\n\nStill the fastest path for many suppliers — see the import feeds article. Export/REST cards on the Stores hub point you to outbound feeds and `/api/v1`.\n"
|
||||
},
|
||||
{
|
||||
"slug": "plans-free-tier-ai-credits",
|
||||
"title": "Plans, Free tier, and AI credits",
|
||||
"category_slugs": ["billing", "plans"],
|
||||
"keywords": [
|
||||
"free plan",
|
||||
"ai credits",
|
||||
"upgrade",
|
||||
"pricing",
|
||||
"starter",
|
||||
"growth",
|
||||
"business",
|
||||
"product limit",
|
||||
"insufficient credits"
|
||||
],
|
||||
"intent_keys": ["billing_plans", "ai_credits", "free_tier"],
|
||||
"is_published": true,
|
||||
"priority_weight": 88,
|
||||
"body_md": "# Plans, Free tier, and AI credits\n\nPublic plans: **Free → Starter → Growth → Business → Enterprise**.\n\n## At a glance\n\n| Plan | SKU cap | Monthly AI credits |\n|------|--------:|-------------------:|\n| Free | 100 | 0 |\n| Starter | 1,000 | 300 |\n| Growth | 10,000 | 2,000 |\n| Business | 100,000 | 10,000 |\n| Enterprise | Unlimited | Large managed pack |\n\n## What Free includes\n\n- Map a feed, clean product data, parse specs, fill attributes\n- EU **EPREL** energy-label enrichment (public data; not billed as AI credits)\n- One manual export feed (marketing packaging)\n- **No** AI titles/descriptions until you upgrade or obtain credits\n\n## AI credits\n\nAI credits pay for steps like generating titles and descriptions. When credits run out or Free has `can_use_ai=false`, AI-only jobs fail with an upgrade/credits error while non-AI cleanup can still run.\n\n## Hitting limits\n\n- **Product limit exceeded** — free space (delete/archive) or upgrade.\n- **Insufficient credits** — wait for the next cycle, upgrade, or use BYOK when your plan allows.\n\nUpgrade from **Plans** / **Billing** (Stripe Checkout on paid self-serve). Enterprise is sales-led.\n"
|
||||
},
|
||||
{
|
||||
"slug": "api-keys-and-public-v1",
|
||||
"title": "API keys and the public /api/v1 catalog",
|
||||
"category_slugs": ["api", "howto"],
|
||||
"keywords": [
|
||||
"api key",
|
||||
"bearer token",
|
||||
"openapi",
|
||||
"public api",
|
||||
"rest api",
|
||||
"documentation",
|
||||
"/api/v1"
|
||||
],
|
||||
"intent_keys": ["api_keys", "public_api", "openapi"],
|
||||
"is_published": true,
|
||||
"priority_weight": 70,
|
||||
"body_md": "# API keys and the public /api/v1 catalog\n\nUse the REST API to automate feeds, products, processing, and exports from your own systems.\n\n## Create a key\n\n1. Open **Settings → API keys**.\n2. Create a key and **copy it immediately** — plaintext is only shown once.\n3. Send `Authorization: Bearer <key>` on `/api/v1/...` requests.\n\nAPI access is plan-gated (`capability.api_access`). Free marketing packaging lists API as not included; paid plans unlock read/full access per plan features.\n\n## Explore the contract\n\n- In-app docs UI: `/docs` (RapiDoc)\n- OpenAPI YAML: `GET /api/v1/openapi.yaml`\n- Health: `GET /api/v1/health` (no key)\n\n## Common calls\n\n- `GET /api/v1/feeds`\n- `GET /api/v1/products`\n- `POST /api/v1/products/process` (items require `ean`)\n- `GET /api/v1/process/{id}`\n- `GET|POST /api/v1/export-feeds`\n\nDashboard session routes under `/api/...` use cookies + CSRF; do not mix session CSRF rules with API-key v1 calls.\n"
|
||||
},
|
||||
{
|
||||
"slug": "troubleshooting-processing-jobs",
|
||||
"title": "Troubleshooting: processing jobs stuck or failing",
|
||||
"category_slugs": ["troubleshooting", "processing"],
|
||||
"keywords": [
|
||||
"stuck",
|
||||
"processing failed",
|
||||
"job not finishing",
|
||||
"worker",
|
||||
"rate limit",
|
||||
"no matching products",
|
||||
"retry",
|
||||
"cancel job",
|
||||
"error tab"
|
||||
],
|
||||
"intent_keys": ["processing_stuck", "job_failed", "worker_down"],
|
||||
"is_published": true,
|
||||
"priority_weight": 92,
|
||||
"body_md": "# Troubleshooting: processing jobs stuck or failing\n\nUse this checklist when Background Tasks never finish or Products stay on **Processing** / **Error**.\n\n## 1. Is the worker running?\n\nEnrichment runs in the **worker**, not only the API process. Locally start `go run ./cmd/worker` (or your deployed worker). Without it, jobs sit queued.\n\n## 2. Read the Products tabs\n\n- **Error** — open the product / job for step notes (AI skipped, EPREL soft-fail, validation).\n- **Needs review** — AI produced content waiting for accept/edit.\n- **Unprocessed** — raw rows never selected for a job.\n\n## 3. Common API / client messages\n\n| Message (concept) | What to do |\n|-------------------|------------|\n| All items must have a valid `ean` | Add barcodes/GTINs to each process item |\n| raw_product_ids required / not found | Select products that belong to your company |\n| too many products | Split the batch |\n| rate limit: too many processing jobs | Wait and retry |\n| job not cancellable / still active | Wait for completion or use supported cancel states |\n| insufficient credits / AI requires upgrade | Upgrade plan or free AI-only steps |\n| product limit exceeded | Reduce SKUs or upgrade |\n\n## 4. AI skipped on Free\n\nIf notes say AI enhance was skipped on Free, that is **expected**. Non-AI normalize/fill can still succeed.\n\n## 5. Cancel / retry\n\nFrom the job UI or API: `POST .../cancel` or `.../retry` on the processing job routes when the job state allows it.\n\nStill stuck? Open a **Support** ticket from the app and include the job id, company name, and approximate start time.\n"
|
||||
},
|
||||
{
|
||||
"slug": "troubleshooting-feeds-and-exports",
|
||||
"title": "Troubleshooting: feeds will not sync or export is empty",
|
||||
"category_slugs": ["troubleshooting", "feeds", "export"],
|
||||
"keywords": [
|
||||
"feed sync failed",
|
||||
"empty export",
|
||||
"mapping missing",
|
||||
"ftp",
|
||||
"cannot sync",
|
||||
"no products",
|
||||
"public export url"
|
||||
],
|
||||
"intent_keys": ["feed_sync_failed", "empty_export", "mapping_problems"],
|
||||
"is_published": true,
|
||||
"priority_weight": 86,
|
||||
"body_md": "# Troubleshooting: feeds and exports\n\n## Feed will not sync\n\n1. Confirm the URL is public **https** (or http in local/dev) — private LAN hosts may be blocked by allowlists.\n2. FTP/FTPS sources are **not supported**; switch to URL or CSV upload.\n3. Open **Map** and ensure mappings are **saved** before expecting processed fields.\n4. Re-run **Extract schema** if the supplier changed XML structure.\n5. Check feed sync job status on the feed page; retry Sync.\n\n## Products missing after sync\n\n- Look under **Products → Unprocessed** (processed tab only shows finished enrichment).\n- Confirm you are in the correct **company** workspace.\n- Verify merge-by-GTIN settings if duplicates seem to disappear.\n\n## Export URL empty or stale\n\n1. Confirm processed products exist for the filters your export template uses.\n2. Click **Generate / Refresh** on the export feed.\n3. Re-copy the public token URL (`/api/public/export-feeds/{token}.csv|.xml`).\n4. Map required channel fields (id, title, price, image_link, GTIN, …).\n\nAmazon-style channel presets beyond Google/Meta are **backlog** — use custom CSV/XML mappings for other marketplaces.\n"
|
||||
},
|
||||
{
|
||||
"slug": "eprel-energy-labels",
|
||||
"title": "EU EPREL energy labels on products",
|
||||
"category_slugs": ["catalog", "compliance"],
|
||||
"keywords": [
|
||||
"eprel",
|
||||
"energy label",
|
||||
"energy class",
|
||||
"eu label",
|
||||
"eprelid",
|
||||
"fiche"
|
||||
],
|
||||
"intent_keys": ["eprel", "energy_label"],
|
||||
"is_published": true,
|
||||
"priority_weight": 65,
|
||||
"body_md": "# EU EPREL energy labels\n\nWhen a feed item includes an EPREL registration id (`EPRELID`, `eprel_id`, …), Descrybe can enrich the processed product with label and energy-class data from the public EU EPREL API.\n\n## Plan gating\n\nEPREL is available on **all plans** (including Free) from a product entitlement perspective — it is public regulatory data and is **not** charged as AI credits. Your platform operator may still disable the enricher (`EPREL_ENABLED`) in the deployment.\n\n## Pipeline position\n\nWorker steps (simplified): normalize → parse_specs → fill_fields → **eprel** → ai_enhance (optional).\n\nFailures are **soft**: the product can still complete; a note may appear on the eprel step.\n\n## Export\n\nMap fields such as `eprel_id`, `eprel_label` / `eprel_label_url`, `eprel_pdf`, `eprel_energy_class`, `eprel_energy_scale` (and aliases like `energy_class`) in your export template.\n"
|
||||
},
|
||||
{
|
||||
"slug": "marketing-campaigns-brand-seo",
|
||||
"title": "Marketing suite: campaigns, brand kit, and SEO",
|
||||
"category_slugs": ["marketing", "howto"],
|
||||
"keywords": [
|
||||
"campaigns",
|
||||
"black friday",
|
||||
"brand kit",
|
||||
"seo",
|
||||
"email marketing",
|
||||
"content calendar",
|
||||
"reviews"
|
||||
],
|
||||
"intent_keys": ["marketing_suite", "campaigns", "seo_brand"],
|
||||
"is_published": true,
|
||||
"priority_weight": 60,
|
||||
"body_md": "# Marketing suite: campaigns, brand, SEO\n\n## Campaigns\n\n1. Open **Campaigns** (`/campaigns`) or prepare a season from **Content calendar**.\n2. Create a campaign (e.g. Black Friday template).\n3. Pick products/categories; optionally build audiences from synced Woo **orders**.\n4. **Generate** — template mode works on Free; **AI generate** needs Starter+ / credits.\n5. Configure **Email sending** (`/integrations/email`), send a test, then schedule/send. Free plans force **dry-run** for live blasts.\n\n## Brand kit\n\n`/brand` — tone, do/don't lists, colors, preferred terms, logo. Applied to AI prompts when brand AI apply is allowed.\n\n## SEO\n\n`/seo` — missing meta, thin/duplicate titles, weak images. Template fill on Free; AI rewrite on paid.\n\n## Reviews\n\nSync Woo reviews from the WooCommerce Reviews tab (`/reviews` redirects there). Shopify native reviews are not available.\n"
|
||||
},
|
||||
{
|
||||
"slug": "in-app-tutorial-and-support",
|
||||
"title": "In-app tutorial and how to contact support",
|
||||
"category_slugs": ["getting-started", "support"],
|
||||
"keywords": [
|
||||
"tutorial",
|
||||
"guided tour",
|
||||
"help",
|
||||
"support ticket",
|
||||
"contact support",
|
||||
"faq"
|
||||
],
|
||||
"intent_keys": ["tutorial", "contact_support", "help_center"],
|
||||
"is_published": true,
|
||||
"priority_weight": 72,
|
||||
"body_md": "# In-app tutorial and contacting support\n\n## Tutorial\n\nFrom **Dashboard** or the header: **Start tutorial** / **Resume** / **Restart**.\n\n- Action steps advance only when you click the highlighted control or complete the real success path.\n- **Pause** or Escape keeps progress; **Skip tour** ends it.\n- Progress is stored in your browser (`localStorage`).\n\nTypical path: Standard Fields → Feeds → Map → Products → Export feeds → Campaigns.\n\n## Support Center\n\nSigned-in users can open **Support** to create tickets and reply in-thread. Staff may answer with help-center articles (like this one) when auto-reply matching is enabled by the platform team.\n\nInclude: company name, approximate time, feed/job/product ids, and screenshots when something fails.\n\n## Sales / demos\n\nPublic marketing contact: **info@descrybe.io**. Demo booking is available from the marketing site CTA.\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* One-shot local bootstrap (any OS):
|
||||
* 1. Copy .env.example → .env if missing
|
||||
* 2. Fill empty TOKEN_SIGNING_SECRET / APP_ENCRYPTION_KEY (local only)
|
||||
* 3. docker compose up -d (Postgres)
|
||||
* 4. wait for healthy DB
|
||||
* 5. goose migrate + sqlc
|
||||
*
|
||||
* Usage: node scripts/setup.mjs
|
||||
* Then: npm install && npm run dev
|
||||
*/
|
||||
import { spawnSync } from "node:child_process";
|
||||
import crypto from "node:crypto";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import {
|
||||
dotenvPath,
|
||||
parseEnvFile,
|
||||
readRootEnvFile,
|
||||
repoRoot,
|
||||
writeRootEnvFile,
|
||||
} from "./root-env.mjs";
|
||||
|
||||
function requireCmd(bin, hint, versionArgs = ["--version"]) {
|
||||
const r = spawnSync(bin, versionArgs, {
|
||||
encoding: "utf8",
|
||||
shell: process.platform === "win32",
|
||||
});
|
||||
if (r.error || r.status !== 0) {
|
||||
console.error(`[setup] missing prerequisite: ${bin}`);
|
||||
if (hint) console.error(` ${hint}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function runNodeScript(rel) {
|
||||
const script = path.join(repoRoot, rel);
|
||||
const r = spawnSync(process.execPath, [script], {
|
||||
cwd: repoRoot,
|
||||
stdio: "inherit",
|
||||
env: process.env,
|
||||
});
|
||||
if (r.status !== 0) process.exit(r.status ?? 1);
|
||||
}
|
||||
|
||||
function runDockerCompose(args) {
|
||||
const r = spawnSync("docker", ["compose", ...args], {
|
||||
cwd: repoRoot,
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
});
|
||||
if (r.status !== 0) process.exit(r.status ?? 1);
|
||||
}
|
||||
|
||||
function ensureEnvFile() {
|
||||
const dest = dotenvPath();
|
||||
if (fs.existsSync(dest)) {
|
||||
console.log(`[setup] using existing ${path.relative(repoRoot, dest)}`);
|
||||
return;
|
||||
}
|
||||
const example = path.join(repoRoot, ".env.example");
|
||||
if (!fs.existsSync(example)) {
|
||||
console.error("[setup] .env.example missing");
|
||||
process.exit(1);
|
||||
}
|
||||
fs.copyFileSync(example, dest);
|
||||
console.log(`[setup] created ${path.relative(repoRoot, dest)} from .env.example`);
|
||||
}
|
||||
|
||||
function fillEmptySecrets() {
|
||||
const text = readRootEnvFile();
|
||||
if (text == null) return;
|
||||
const parsed = parseEnvFile(text);
|
||||
const need = [];
|
||||
for (const key of ["TOKEN_SIGNING_SECRET", "APP_ENCRYPTION_KEY"]) {
|
||||
if (!parsed[key] || !String(parsed[key]).trim()) need.push(key);
|
||||
}
|
||||
if (need.length === 0) return;
|
||||
|
||||
let next = text;
|
||||
for (const key of need) {
|
||||
const secret = crypto.randomBytes(32).toString("hex");
|
||||
const lineRe = new RegExp(`^(\\s*${key}\\s*=\\s*).*$`, "m");
|
||||
if (lineRe.test(next)) {
|
||||
next = next.replace(lineRe, `$1${secret}`);
|
||||
} else {
|
||||
next = `${next.trimEnd()}\n${key}=${secret}\n`;
|
||||
}
|
||||
console.log(`[setup] generated local ${key} (not committed)`);
|
||||
}
|
||||
writeRootEnvFile(next);
|
||||
}
|
||||
|
||||
console.log("==> Descrybe local setup");
|
||||
requireCmd("node", "Install Node.js 20+ from https://nodejs.org/");
|
||||
// Go uses `go version`, not `go --version`.
|
||||
requireCmd("go", "Install Go 1.25+ from https://go.dev/dl/", ["version"]);
|
||||
requireCmd("docker", "Install Docker Desktop / Docker Engine with Compose v2");
|
||||
|
||||
ensureEnvFile();
|
||||
fillEmptySecrets();
|
||||
|
||||
console.log("==> docker compose up -d (Postgres on host :5433)");
|
||||
runDockerCompose(["up", "-d"]);
|
||||
|
||||
console.log("==> wait for Postgres");
|
||||
runNodeScript("scripts/wait-postgres.mjs");
|
||||
|
||||
console.log("==> migrate");
|
||||
runNodeScript("scripts/migrate.mjs");
|
||||
|
||||
console.log("");
|
||||
console.log("Setup complete.");
|
||||
console.log("Next:");
|
||||
console.log(" npm install");
|
||||
console.log(" npm run dev # API :28471 + web :28472 + worker");
|
||||
console.log(" npm run seed # optional demo login");
|
||||
console.log(" npm run health # /healthz + /readyz (needs worker)");
|
||||
console.log("");
|
||||
console.log("Web: http://localhost:28472");
|
||||
console.log("API: http://localhost:28471");
|
||||
@@ -0,0 +1,115 @@
|
||||
# Staging auth rehearsal helper (Windows / Laragon).
|
||||
# Docs: docs/staging-auth-rehearsal.md
|
||||
#
|
||||
# Does NOT read repo `.env` or any live secrets file.
|
||||
# Requires DATABASE_URL in the environment (or -DatabaseUrl).
|
||||
#
|
||||
# Examples:
|
||||
# $env:DATABASE_URL = "postgres://descrybe:descrybe@localhost:5433/descrybe?sslmode=disable"
|
||||
# .\scripts\staging-auth-rehearsal.ps1 # print SQL + next steps
|
||||
# .\scripts\staging-auth-rehearsal.ps1 -IssueInvites # also re-issue invites (no SMTP)
|
||||
# .\scripts\staging-auth-rehearsal.ps1 -IssueInvites -DryRun
|
||||
|
||||
param(
|
||||
[string]$DatabaseUrl = $env:DATABASE_URL,
|
||||
[string]$WebOrigin = $env:WEB_ORIGIN,
|
||||
[string]$MapsDir = "",
|
||||
[switch]$IssueInvites,
|
||||
[switch]$DryRun
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$Root = Resolve-Path (Join-Path $PSScriptRoot "..")
|
||||
$ApiDir = Join-Path $Root "apps\api"
|
||||
if (-not $MapsDir) {
|
||||
$MapsDir = Join-Path $Root "artifacts"
|
||||
}
|
||||
|
||||
Write-Host "==> Staging auth rehearsal (no SMTP / link-copy path)"
|
||||
Write-Host " Docs: docs/staging-auth-rehearsal.md"
|
||||
Write-Host ""
|
||||
|
||||
if (-not $DatabaseUrl) {
|
||||
Write-Host "DATABASE_URL is not set. Export it in this shell only (do not paste production secrets into the repo)."
|
||||
Write-Host 'Example: $env:DATABASE_URL = "postgres://descrybe:descrybe@localhost:5433/descrybe?sslmode=disable"'
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
Write-Host "=== 1. Promote company admin (preferred: migrator CLI; dry-run first) ==="
|
||||
Write-Host @"
|
||||
cd apps/api
|
||||
go run ./cmd/migrator -list-member-memberships -postgres "`$DATABASE_URL"
|
||||
go run ./cmd/migrator -promote-company-admins -email '<real-email>' -dry-run -postgres "`$DATABASE_URL"
|
||||
go run ./cmd/migrator -promote-company-admins -email '<real-email>' -confirm -postgres "`$DATABASE_URL"
|
||||
|
||||
# SQL alternate (fill UUIDs):
|
||||
-- List
|
||||
SELECT u.id AS user_id, u.email, u.must_set_password, u.is_platform_admin,
|
||||
m.company_id, c.name AS company_name, m.role, m.status
|
||||
FROM memberships m
|
||||
JOIN users u ON u.id = m.user_id
|
||||
JOIN companies c ON c.id = m.company_id
|
||||
WHERE m.status = 'active'
|
||||
ORDER BY c.name, u.email;
|
||||
|
||||
-- Promote (fill UUIDs)
|
||||
UPDATE memberships
|
||||
SET role = 'admin'
|
||||
WHERE user_id = '<user-uuid>'
|
||||
AND company_id = '<company-uuid>'
|
||||
AND status = 'active'
|
||||
RETURNING user_id, company_id, role;
|
||||
"@
|
||||
Write-Host ""
|
||||
|
||||
if (-not $IssueInvites) {
|
||||
Write-Host "=== 2. Re-issue set-password (skipped) ==="
|
||||
Write-Host "Re-run with -IssueInvites after emails are real (not *@legacy.local)."
|
||||
Write-Host " .\scripts\staging-auth-rehearsal.ps1 -IssueInvites"
|
||||
Write-Host ""
|
||||
Write-Host "=== 3. Login smoke (manual) ==="
|
||||
Write-Host "Copy one URL from migrator stdout or artifacts/password_invites.json"
|
||||
Write-Host "-> open /accept-invite -> set password -> /login -> confirm must_set_password=false"
|
||||
exit 0
|
||||
}
|
||||
|
||||
if (-not $DatabaseUrl) {
|
||||
Write-Error "DATABASE_URL is required for -IssueInvites"
|
||||
}
|
||||
|
||||
if ($WebOrigin) {
|
||||
$env:WEB_ORIGIN = $WebOrigin
|
||||
Write-Host "WEB_ORIGIN=$WebOrigin"
|
||||
} else {
|
||||
Write-Host "WEB_ORIGIN unset -> migrator default http://localhost:28472"
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $MapsDir | Out-Null
|
||||
Set-Location $ApiDir
|
||||
|
||||
$mapsRel = Resolve-Path $MapsDir
|
||||
Write-Host "=== 2. Re-issue set-password invites (maps-dir=$mapsRel) ==="
|
||||
$migratorArgs = @(
|
||||
"./cmd/migrator",
|
||||
"-issue-set-password-invites",
|
||||
"-postgres", $DatabaseUrl,
|
||||
"-maps-dir", $mapsRel
|
||||
)
|
||||
if ($DryRun) {
|
||||
$migratorArgs += "-dry-run"
|
||||
Write-Host "(dry-run: no invites written)"
|
||||
}
|
||||
|
||||
& go run @migratorArgs
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "migrator -issue-set-password-invites failed (exit $LASTEXITCODE)"
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=== 3. Login smoke (manual - no SMTP) ==="
|
||||
Write-Host "1. Copy one email<TAB>url line from stdout above (or $mapsRel\password_invites.json)."
|
||||
Write-Host "2. Open the URL -> set password (at least 8 chars)."
|
||||
Write-Host "3. Sign in at /login with the same email."
|
||||
Write-Host '4. SQL: SELECT email, must_set_password FROM users WHERE lower(email)=lower(''<smoke-email>'');'
|
||||
Write-Host "Do NOT run cmd/mailhooks for this rehearsal."
|
||||
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env bash
|
||||
# Staging auth rehearsal helper.
|
||||
# Docs: docs/staging-auth-rehearsal.md
|
||||
#
|
||||
# Does NOT read repo .env or any live secrets file.
|
||||
# Requires DATABASE_URL in the environment for -issue.
|
||||
#
|
||||
# Examples:
|
||||
# export DATABASE_URL='postgres://descrybe:descrybe@localhost:5433/descrybe?sslmode=disable'
|
||||
# ./scripts/staging-auth-rehearsal.sh # print SQL + next steps
|
||||
# ./scripts/staging-auth-rehearsal.sh --issue # also re-issue invites (no SMTP)
|
||||
# ./scripts/staging-auth-rehearsal.sh --issue --dry-run
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
API_DIR="$ROOT/apps/api"
|
||||
MAPS_DIR="${MAPS_DIR:-$ROOT/artifacts}"
|
||||
ISSUE=0
|
||||
DRY_RUN=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--issue) ISSUE=1; shift ;;
|
||||
--dry-run) DRY_RUN=1; shift ;;
|
||||
--maps-dir) MAPS_DIR="$2"; shift 2 ;;
|
||||
-h|--help)
|
||||
sed -n '1,20p' "$0"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown arg: $1" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo "==> Staging auth rehearsal (no SMTP / link-copy path)"
|
||||
echo " Docs: docs/staging-auth-rehearsal.md"
|
||||
echo
|
||||
|
||||
if [[ -z "${DATABASE_URL:-}" ]]; then
|
||||
echo "DATABASE_URL is not set. Export it in this shell only (do not paste production secrets into the repo)."
|
||||
echo "Example: export DATABASE_URL='postgres://descrybe:descrybe@localhost:5433/descrybe?sslmode=disable'"
|
||||
echo
|
||||
fi
|
||||
|
||||
cat <<'EOF'
|
||||
=== 1. Promote company admin (preferred: migrator CLI; dry-run first) ===
|
||||
cd apps/api
|
||||
go run ./cmd/migrator -list-member-memberships -postgres "$DATABASE_URL"
|
||||
go run ./cmd/migrator -promote-company-admins -email '<real-email>' -dry-run -postgres "$DATABASE_URL"
|
||||
go run ./cmd/migrator -promote-company-admins -email '<real-email>' -confirm -postgres "$DATABASE_URL"
|
||||
|
||||
# SQL alternate (fill UUIDs):
|
||||
-- List
|
||||
SELECT u.id AS user_id, u.email, u.must_set_password, u.is_platform_admin,
|
||||
m.company_id, c.name AS company_name, m.role, m.status
|
||||
FROM memberships m
|
||||
JOIN users u ON u.id = m.user_id
|
||||
JOIN companies c ON c.id = m.company_id
|
||||
WHERE m.status = 'active'
|
||||
ORDER BY c.name, u.email;
|
||||
|
||||
-- Promote (fill UUIDs)
|
||||
UPDATE memberships
|
||||
SET role = 'admin'
|
||||
WHERE user_id = '<user-uuid>'
|
||||
AND company_id = '<company-uuid>'
|
||||
AND status = 'active'
|
||||
RETURNING user_id, company_id, role;
|
||||
|
||||
EOF
|
||||
|
||||
if [[ "$ISSUE" -eq 0 ]]; then
|
||||
echo "=== 2. Re-issue set-password (skipped) ==="
|
||||
echo "Re-run with --issue after emails are real (not *@legacy.local)."
|
||||
echo " ./scripts/staging-auth-rehearsal.sh --issue"
|
||||
echo
|
||||
echo "=== 3. Login smoke (manual) ==="
|
||||
echo "Copy one URL from migrator stdout or artifacts/password_invites.json"
|
||||
echo "-> open /accept-invite -> set password -> /login -> confirm must_set_password=false"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -z "${DATABASE_URL:-}" ]]; then
|
||||
echo "DATABASE_URL is required for --issue" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "${WEB_ORIGIN:-}" ]]; then
|
||||
echo "WEB_ORIGIN=$WEB_ORIGIN"
|
||||
else
|
||||
echo "WEB_ORIGIN unset -> migrator default http://localhost:28472"
|
||||
fi
|
||||
|
||||
mkdir -p "$MAPS_DIR"
|
||||
cd "$API_DIR"
|
||||
|
||||
echo "=== 2. Re-issue set-password invites (maps-dir=$MAPS_DIR) ==="
|
||||
ARGS=(-issue-set-password-invites -postgres "$DATABASE_URL" -maps-dir "$MAPS_DIR")
|
||||
if [[ "$DRY_RUN" -eq 1 ]]; then
|
||||
ARGS+=(-dry-run)
|
||||
echo "(dry-run: no invites written)"
|
||||
fi
|
||||
|
||||
go run ./cmd/migrator "${ARGS[@]}"
|
||||
|
||||
echo
|
||||
echo "=== 3. Login smoke (manual - no SMTP) ==="
|
||||
echo "1. Copy one email<TAB>url line from stdout above (or $MAPS_DIR/password_invites.json)."
|
||||
echo "2. Open the URL -> set password (at least 8 chars)."
|
||||
echo "3. Sign in at /login with the same email."
|
||||
echo "4. SQL: SELECT email, must_set_password FROM users WHERE lower(email)=lower('<smoke-email>');"
|
||||
echo "Do NOT run cmd/mailhooks for this rehearsal."
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env node
|
||||
/** Run: node scripts/test-assistant.mjs */
|
||||
import { spawnSync } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const testFile = path.join(root, "apps/web/src/lib/assistant/assistant.test.ts");
|
||||
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
["--experimental-strip-types", "--test", testFile],
|
||||
{ cwd: root, stdio: "inherit", env: process.env }
|
||||
);
|
||||
process.exit(result.status ?? 1);
|
||||
@@ -0,0 +1,48 @@
|
||||
# Legacy v1 process smoke
|
||||
|
||||
Sequential create/process + poll against the legacy public surface:
|
||||
|
||||
1. `POST /api/v1/products/process` with `items[{ean,…}]`
|
||||
2. `GET /api/v1/products/process/{process_id}` until `COMPLETED` / `FAILED` / `CANCELLED`
|
||||
|
||||
Not a load test (that is `scripts/api-load-smoke`). One job, low poll rate.
|
||||
|
||||
## Auth (no secrets in this folder)
|
||||
|
||||
Set **`API_KEY`** (or `-key`). For local Platform Demo, use the seeded key documented in [`docs/demo-user.md`](../../docs/demo-user.md) (prefix `dk_demo_lo…`). Do not commit other keys.
|
||||
|
||||
## Smoke (local)
|
||||
|
||||
API on `:28471` and a **worker** must be running (`npm run dev` or `scripts/run-api.ps1` + worker).
|
||||
|
||||
```powershell
|
||||
$env:API_BASE = "http://127.0.0.1:28471"
|
||||
$env:API_KEY = "<paste from docs/demo-user.md>"
|
||||
cd f:\laragon\www\_MY\descrybe-v2\scripts\v1-process-smoke
|
||||
go run .
|
||||
```
|
||||
|
||||
Defaults: one synthetic EAN (`8700999000001`), `processing_type=full`, poll every 2s, wait up to 3m.
|
||||
|
||||
```powershell
|
||||
go run . -eans 8806088803005,8700000000002 -processing-type full -wait 5m
|
||||
```
|
||||
|
||||
Exit `0` on `COMPLETED`, `1` on failure/timeout, `2` on bad flags / missing key.
|
||||
|
||||
## Cleanup leftover Demo smoke EANs
|
||||
|
||||
There is **no** product soft-delete HTTP API (`GET`/`PATCH` only). After a smoke run, leftover `8700999...` rows stay on **Platform Demo** only (never A1).
|
||||
|
||||
Safe paths (Demo only; both refuse A1):
|
||||
|
||||
```powershell
|
||||
# SQL (preview SELECT is in the script header comments)
|
||||
psql $env:DATABASE_URL -v ON_ERROR_STOP=1 -f scripts/cleanup-process-smoke-eans.sql
|
||||
|
||||
# Or via seed-demo
|
||||
cd apps/api
|
||||
go run ./cmd/seed-demo -purge-smoke-eans
|
||||
```
|
||||
|
||||
Related: [`docs/live-public-api-e2e.md`](../../docs/live-public-api-e2e.md), [`docs/demo-user.md`](../../docs/demo-user.md).
|
||||
@@ -0,0 +1,3 @@
|
||||
module github.com/descrybe/descrybe-v2/scripts/v1-process-smoke
|
||||
|
||||
go 1.22
|
||||
@@ -0,0 +1,277 @@
|
||||
// Command v1-process-smoke: low-concurrency legacy v1 create/process + poll.
|
||||
//
|
||||
// Contract (see docs/live-public-api-e2e.md):
|
||||
//
|
||||
// POST /api/v1/products/process { items:[{ean,…}], processing_type }
|
||||
// GET /api/v1/products/process/{process_id} until COMPLETED|FAILED|CANCELLED
|
||||
//
|
||||
// Auth: set API_KEY (or -key). Local seed key is documented in docs/demo-user.md —
|
||||
// do not commit other secrets. Default base: http://127.0.0.1:28471 (HTTP_ADDR).
|
||||
//
|
||||
// Usage (API + worker listening):
|
||||
//
|
||||
// $env:API_KEY = "<from docs/demo-user.md>"
|
||||
// cd scripts/v1-process-smoke && go run .
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultBase = "http://127.0.0.1:28471"
|
||||
defaultPoll = 2 * time.Second
|
||||
defaultTimeout = 10 * time.Second
|
||||
defaultWait = 3 * time.Minute
|
||||
defaultProcType = "full"
|
||||
defaultSmokeEAN = "8700999000001"
|
||||
)
|
||||
|
||||
type envelope struct {
|
||||
Data json.RawMessage `json:"data"`
|
||||
Error *struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
type startData struct {
|
||||
ProcessID string `json:"process_id"`
|
||||
Message string `json:"message"`
|
||||
TotalItems int `json:"total_items"`
|
||||
ProcessedItems int `json:"processed_items"`
|
||||
Errors []string `json:"errors"`
|
||||
}
|
||||
|
||||
type statusData struct {
|
||||
Status string `json:"status"`
|
||||
ProcessID string `json:"process_id"`
|
||||
ProcessingType json.RawMessage `json:"processing_type"`
|
||||
Items []any `json:"items"`
|
||||
TotalItems int `json:"total_items"`
|
||||
Error string `json:"error"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
base := flag.String("base", envOr("API_BASE", defaultBase), "API base URL (no trailing slash)")
|
||||
key := flag.String("key", strings.TrimSpace(os.Getenv("API_KEY")), "API key (Bearer); prefer env API_KEY")
|
||||
eans := flag.String("eans", envOr("SMOKE_EANS", defaultSmokeEAN), "comma-separated EANs to upsert+process")
|
||||
title := flag.String("title", envOr("SMOKE_TITLE", "v1-process-smoke sample"), "title set on each created item")
|
||||
ptype := flag.String("processing-type", envOr("SMOKE_PROCESSING_TYPE", defaultProcType), "processing_type (full|category|title|…)")
|
||||
pollEvery := flag.Duration("poll", defaultPoll, "status poll interval")
|
||||
waitFor := flag.Duration("wait", defaultWait, "max time to wait for terminal status")
|
||||
timeout := flag.Duration("timeout", defaultTimeout, "per-request HTTP timeout")
|
||||
flag.Parse()
|
||||
|
||||
apiKey := strings.TrimSpace(*key)
|
||||
if apiKey == "" {
|
||||
fmt.Fprintln(os.Stderr, "API_KEY (or -key) is required.")
|
||||
fmt.Fprintln(os.Stderr, "Local seed key: docs/demo-user.md (Platform Demo; prefix dk_demo_lo…).")
|
||||
fmt.Fprintln(os.Stderr, "Example: $env:API_KEY = \"…\" ; go run .")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
eanList := splitCSV(*eans)
|
||||
if len(eanList) == 0 {
|
||||
fmt.Fprintln(os.Stderr, "-eans must list at least one EAN")
|
||||
os.Exit(2)
|
||||
}
|
||||
if *pollEvery <= 0 || *waitFor <= 0 {
|
||||
fmt.Fprintln(os.Stderr, "-poll and -wait must be > 0")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
baseURL := strings.TrimRight(strings.TrimSpace(*base), "/")
|
||||
client := &http.Client{Timeout: *timeout}
|
||||
|
||||
items := make([]map[string]string, 0, len(eanList))
|
||||
for _, ean := range eanList {
|
||||
items = append(items, map[string]string{
|
||||
"ean": ean,
|
||||
"title": *title,
|
||||
})
|
||||
}
|
||||
body, err := json.Marshal(map[string]any{
|
||||
"processing_type": *ptype,
|
||||
"items": items,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "marshal start body: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("v1-process-smoke base=%s items=%d processing_type=%s poll=%s wait=%s\n",
|
||||
baseURL, len(items), *ptype, pollEvery.String(), waitFor.String())
|
||||
fmt.Println("ASSUMPTION: worker must be running or jobs stay PENDING/PROCESSING until timeout.")
|
||||
|
||||
startURL := baseURL + "/api/v1/products/process"
|
||||
raw, code, err := doJSON(client, http.MethodPost, startURL, apiKey, body)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "POST %s: %v\n", startURL, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
start, err := decodeStart(raw, code)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "POST start failed (HTTP %d): %v\nbody=%s\n", code, err, truncate(string(raw), 400))
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("started process_id=%s total_items=%d processed_items=%d %s\n",
|
||||
start.ProcessID, start.TotalItems, start.ProcessedItems, start.Message)
|
||||
if len(start.Errors) > 0 {
|
||||
fmt.Printf("item warnings: %s\n", strings.Join(start.Errors, "; "))
|
||||
}
|
||||
|
||||
deadline := time.Now().Add(*waitFor)
|
||||
statusURL := baseURL + "/api/v1/products/process/" + start.ProcessID
|
||||
for {
|
||||
raw, code, err := doJSON(client, http.MethodGet, statusURL, apiKey, nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "GET %s: %v\n", statusURL, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
st, err := decodeStatus(raw, code)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "GET status failed (HTTP %d): %v\nbody=%s\n", code, err, truncate(string(raw), 400))
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("poll status=%s items=%d\n", st.Status, len(st.Items))
|
||||
|
||||
switch strings.ToUpper(st.Status) {
|
||||
case "COMPLETED":
|
||||
if st.Message != "" {
|
||||
fmt.Println(st.Message)
|
||||
}
|
||||
fmt.Printf("ok process_id=%s total_items=%d\n", st.ProcessID, st.TotalItems)
|
||||
os.Exit(0)
|
||||
case "FAILED":
|
||||
fmt.Fprintf(os.Stderr, "process failed: %s\n", firstNonEmpty(st.Error, st.Message, "unknown"))
|
||||
os.Exit(1)
|
||||
case "CANCELLED", "CANCELED":
|
||||
fmt.Fprintln(os.Stderr, "process cancelled")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if time.Now().After(deadline) {
|
||||
fmt.Fprintf(os.Stderr, "timeout after %s (last status=%s). Is the worker running?\n", waitFor.String(), st.Status)
|
||||
os.Exit(1)
|
||||
}
|
||||
time.Sleep(*pollEvery)
|
||||
}
|
||||
}
|
||||
|
||||
func doJSON(client *http.Client, method, url, apiKey string, body []byte) ([]byte, int, error) {
|
||||
var rdr io.Reader
|
||||
if body != nil {
|
||||
rdr = bytes.NewReader(body)
|
||||
}
|
||||
req, err := http.NewRequest(method, url, rdr)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+apiKey)
|
||||
req.Header.Set("Accept", "application/json")
|
||||
if body != nil {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
raw, err := io.ReadAll(io.LimitReader(res.Body, 1<<20))
|
||||
if err != nil {
|
||||
return nil, res.StatusCode, err
|
||||
}
|
||||
return raw, res.StatusCode, nil
|
||||
}
|
||||
|
||||
func decodeStart(raw []byte, code int) (startData, error) {
|
||||
var out startData
|
||||
data, err := unwrapData(raw, code)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
if err := json.Unmarshal(data, &out); err != nil {
|
||||
return out, err
|
||||
}
|
||||
if strings.TrimSpace(out.ProcessID) == "" {
|
||||
return out, fmt.Errorf("missing data.process_id")
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func decodeStatus(raw []byte, code int) (statusData, error) {
|
||||
var out statusData
|
||||
data, err := unwrapData(raw, code)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
if err := json.Unmarshal(data, &out); err != nil {
|
||||
return out, err
|
||||
}
|
||||
if strings.TrimSpace(out.Status) == "" {
|
||||
return out, fmt.Errorf("missing data.status")
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func unwrapData(raw []byte, code int) (json.RawMessage, error) {
|
||||
var env envelope
|
||||
if err := json.Unmarshal(raw, &env); err != nil {
|
||||
return nil, fmt.Errorf("invalid JSON: %w", err)
|
||||
}
|
||||
if env.Error != nil {
|
||||
return nil, fmt.Errorf("%s: %s", env.Error.Code, env.Error.Message)
|
||||
}
|
||||
if code < 200 || code >= 300 {
|
||||
return nil, fmt.Errorf("unexpected HTTP %d", code)
|
||||
}
|
||||
if len(env.Data) == 0 || string(env.Data) == "null" {
|
||||
return nil, fmt.Errorf("missing data")
|
||||
}
|
||||
return env.Data, nil
|
||||
}
|
||||
|
||||
func splitCSV(s string) []string {
|
||||
parts := strings.Split(s, ",")
|
||||
out := make([]string, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
p = strings.TrimSpace(p)
|
||||
if p != "" {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func envOr(key, fallback string) string {
|
||||
if v := strings.TrimSpace(os.Getenv(key)); v != "" {
|
||||
return v
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
func firstNonEmpty(vals ...string) string {
|
||||
for _, v := range vals {
|
||||
if strings.TrimSpace(v) != "" {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func truncate(s string, n int) string {
|
||||
if len(s) <= n {
|
||||
return s
|
||||
}
|
||||
return s[:n] + "…"
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Wait until docker-compose Postgres is accepting connections.
|
||||
* Usage: node scripts/wait-postgres.mjs
|
||||
*/
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { setTimeout as delay } from "node:timers/promises";
|
||||
import { repoRoot } from "./root-env.mjs";
|
||||
|
||||
const maxAttempts = Number(process.env.DESCRYBE_DB_WAIT_ATTEMPTS || 60);
|
||||
const sleepMs = Number(process.env.DESCRYBE_DB_WAIT_MS || 1000);
|
||||
|
||||
for (let i = 1; i <= maxAttempts; i++) {
|
||||
const r = spawnSync(
|
||||
"docker",
|
||||
[
|
||||
"compose",
|
||||
"exec",
|
||||
"-T",
|
||||
"postgres",
|
||||
"pg_isready",
|
||||
"-U",
|
||||
"descrybe",
|
||||
"-d",
|
||||
"descrybe",
|
||||
],
|
||||
{
|
||||
cwd: repoRoot,
|
||||
encoding: "utf8",
|
||||
shell: process.platform === "win32",
|
||||
},
|
||||
);
|
||||
if (r.status === 0) {
|
||||
console.log(`[db] postgres ready (attempt ${i})`);
|
||||
process.exit(0);
|
||||
}
|
||||
process.stdout.write(`[db] waiting for postgres… (${i}/${maxAttempts})\r`);
|
||||
await delay(sleepMs);
|
||||
}
|
||||
|
||||
console.error("\n[db] postgres did not become ready in time");
|
||||
console.error("Check: docker compose ps && docker compose logs postgres");
|
||||
process.exit(1);
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Force process env KEY=VAL pairs, then spawn the command after `--`.
|
||||
* Values passed here always override so children that load monorepo `.env`
|
||||
* but skip pre-set keys (run-in-dir / Vite) pick up these ports.
|
||||
*
|
||||
* Usage: node scripts/with-forced-env.mjs KEY=val KEY=val -- command [args...]
|
||||
*/
|
||||
import { spawn } from "node:child_process";
|
||||
|
||||
const sep = process.argv.indexOf("--");
|
||||
if (sep < 0 || sep === process.argv.length - 1) {
|
||||
console.error("Usage: node scripts/with-forced-env.mjs KEY=val -- command [args...]");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const env = { ...process.env };
|
||||
for (const raw of process.argv.slice(2, sep)) {
|
||||
const eq = raw.indexOf("=");
|
||||
if (eq <= 0) {
|
||||
console.error(`Invalid KEY=val: ${raw}`);
|
||||
process.exit(1);
|
||||
}
|
||||
env[raw.slice(0, eq)] = raw.slice(eq + 1);
|
||||
}
|
||||
|
||||
const [command, ...args] = process.argv.slice(sep + 1);
|
||||
const child = spawn(command, args, {
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
env,
|
||||
});
|
||||
|
||||
child.on("exit", (code, signal) => {
|
||||
if (signal) {
|
||||
process.kill(process.pid, signal);
|
||||
return;
|
||||
}
|
||||
process.exit(code ?? 1);
|
||||
});
|
||||
Reference in New Issue
Block a user