Naprawa pobierania komponentów - aktualizacja ciasteczek i konfiguracji
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.env
|
||||||
|
node_modules/
|
||||||
|
pages/
|
||||||
|
.DS_Store
|
||||||
|
*.log
|
||||||
6
.prettierignore
Normal file
6
.prettierignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
node_modules
|
||||||
|
.env
|
||||||
|
.env.example
|
||||||
|
docs
|
||||||
|
dist
|
||||||
|
build
|
||||||
25
.prettierrc
Normal file
25
.prettierrc
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 100,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"arrowParens": "always",
|
||||||
|
"proseWrap": "preserve",
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "*.xml",
|
||||||
|
"options": {
|
||||||
|
"parser": "babel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": "*.less",
|
||||||
|
"options": {
|
||||||
|
"parser": "less"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
7
.stylelintrc.json
Normal file
7
.stylelintrc.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "stylelint-config-standard-less",
|
||||||
|
"rules": {
|
||||||
|
"no-empty-source": null,
|
||||||
|
"no-duplicate-selectors": true
|
||||||
|
}
|
||||||
|
}
|
||||||
7079
component request.md
Normal file
7079
component request.md
Normal file
File diff suppressed because it is too large
Load Diff
227
components request
Normal file
227
components request
Normal file
File diff suppressed because one or more lines are too long
5
docs/.env.example
Normal file
5
docs/.env.example
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Umieść tutaj swój AUTH_TOKEN i TEMPLATE_ID
|
||||||
|
|
||||||
|
AUTH_TOKEN=your_token_here
|
||||||
|
TEMPLATE_ID=your_template_id_here
|
||||||
|
API_URL=https://composer.idosell.com/api
|
||||||
112
docs/LINTING.md
Normal file
112
docs/LINTING.md
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
# Prettier i Stylelint - Formatowanie i Walidacja
|
||||||
|
|
||||||
|
Projekt zawiera konfigurację do formatowania i walidacji kodu.
|
||||||
|
|
||||||
|
## Zainstalowane narzędzia
|
||||||
|
|
||||||
|
- **Prettier** - formatowanie kodu (JS, XML, LESS, JSON)
|
||||||
|
- **Stylelint** - walidacja LESS
|
||||||
|
|
||||||
|
## Komendy
|
||||||
|
|
||||||
|
### Formatowanie
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Sformatuj wszystkie pliki (JS, XML, LESS, JSON)
|
||||||
|
npm run format
|
||||||
|
|
||||||
|
# Sprawdź czy pliki są sformatowane
|
||||||
|
npm run format:check
|
||||||
|
```
|
||||||
|
|
||||||
|
### Walidacja
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Sprawdź LESS w pages/
|
||||||
|
npm run lint:less
|
||||||
|
```
|
||||||
|
|
||||||
|
## Konfiguracje
|
||||||
|
|
||||||
|
### `.prettierrc` - Prettier
|
||||||
|
|
||||||
|
- Print Width: 100
|
||||||
|
- Tab Width: 2
|
||||||
|
- Semicolons: włączone
|
||||||
|
- Single Quotes: wyłączone (używamy `"`)
|
||||||
|
- Trailing Commas: `es5`
|
||||||
|
|
||||||
|
### `.stylelintrc.json` - Stylelint
|
||||||
|
|
||||||
|
Waliduje LESS files:
|
||||||
|
- Reguły standardowe + Stylelint Less
|
||||||
|
- Indentacja: 2 spacje
|
||||||
|
- Sprawdzanie kolorów hex
|
||||||
|
- Sprawdzanie duplikowanych selektorów
|
||||||
|
|
||||||
|
## Obsługa Custom XML
|
||||||
|
|
||||||
|
Pliki XML zawierają custom tagi:
|
||||||
|
- `<iai:variable>`
|
||||||
|
- `<iaixsl:value-of>`
|
||||||
|
- `<iaixsl:for-each>`
|
||||||
|
- itd.
|
||||||
|
|
||||||
|
Prettier je prawidłowo formatuje bez błędów.
|
||||||
|
|
||||||
|
## Obsługa HTML Entities
|
||||||
|
|
||||||
|
Prettier automatycznie konwertuje:
|
||||||
|
- `<` → `<`
|
||||||
|
- `>` → `>`
|
||||||
|
- `&` → `&`
|
||||||
|
|
||||||
|
(dla XML i HTML files)
|
||||||
|
|
||||||
|
## VS Code Integration
|
||||||
|
|
||||||
|
Zainstaluj rozszerzenia:
|
||||||
|
- **Prettier - Code formatter** (esbenp.prettier-vscode)
|
||||||
|
- **Stylelint** (stylelint.vscode-plugin)
|
||||||
|
|
||||||
|
W VS Code możesz formatować na save:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ignorowanie plików
|
||||||
|
|
||||||
|
### `.prettierignore`
|
||||||
|
```
|
||||||
|
node_modules
|
||||||
|
.env
|
||||||
|
.env.example
|
||||||
|
docs
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Przykład: Formatowanie komponenty
|
||||||
|
|
||||||
|
Plik przed:
|
||||||
|
```xml
|
||||||
|
<xml><iai:variable name="test" select="value" /></xml>
|
||||||
|
```
|
||||||
|
|
||||||
|
Po `npm run format`:
|
||||||
|
```xml
|
||||||
|
<xml><iai:variable name="test" select="value" /></xml>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "Parser error" w Prettier
|
||||||
|
- Upewnij się że `.prettierrc` ma poprawną konfigurację
|
||||||
|
- Prettier potrzebuje prawidłowego rozszerzenia pliku (`.xml`, `.less`, `.js`)
|
||||||
|
|
||||||
|
### Stylelint nie sprawdza LESS
|
||||||
|
- Zainstaluj `postcss` i `postcss-less`
|
||||||
|
- Sprawdź że `.stylelintrc.json` ma `postcss-less` w overrides
|
||||||
|
|
||||||
69
docs/README.md
Normal file
69
docs/README.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# 🚀 Composer Builder
|
||||||
|
|
||||||
|
Szybko pobiera strony z szablonów iDoSell i tworzy strukturę folderów do edycji.
|
||||||
|
|
||||||
|
## ⚡ Szybki Start
|
||||||
|
|
||||||
|
### 1. Pierwsza konfiguracja
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run setup
|
||||||
|
```
|
||||||
|
|
||||||
|
Skrypt będzie pytać o dane do API interaktywnie.
|
||||||
|
|
||||||
|
### 2. Pobierz strony
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run template:pull
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📖 Komendy
|
||||||
|
|
||||||
|
| Komenda | Opis |
|
||||||
|
| -------------------------------- | --------------------------------------- |
|
||||||
|
| `npm run setup` | Konfiguracja - wprowadzenie tokena i ID |
|
||||||
|
| `npm run template:pull` | Pobierz WSZYSTKIE komponenty |
|
||||||
|
| `npm run template:pull --custom` | Pobierz TYLKO komponenty typu custom |
|
||||||
|
| `npm run template:config` | Wyświetl aktualną konfigurację |
|
||||||
|
|
||||||
|
## 🔐 Gdzie znaleźć dane?
|
||||||
|
|
||||||
|
### AUTH_TOKEN (monit_token)
|
||||||
|
|
||||||
|
1. Zaloguj się do [composer.idosell.com](https://composer.idosell.com)
|
||||||
|
2. Otwórz DevTools: **F12**
|
||||||
|
3. Idź na: **Application → Cookies**
|
||||||
|
4. Szukaj: **monit_token**
|
||||||
|
5. Skopiuj wartość
|
||||||
|
|
||||||
|
### TEMPLATE_ID
|
||||||
|
|
||||||
|
- URL szablonu: `https://composer.idosell.com/compositions/xxxxx.12345`
|
||||||
|
- TEMPLATE_ID = **12345** (część po kropce)
|
||||||
|
|
||||||
|
To polecenie:
|
||||||
|
|
||||||
|
- Pobierze listę stron z szablonu
|
||||||
|
- Utworzy folder dla każdej strony
|
||||||
|
- Wygeneruje plik `config.json` w każdym folderze
|
||||||
|
|
||||||
|
### Konfiguracja
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run template:config
|
||||||
|
```
|
||||||
|
|
||||||
|
Wyświetli obecną konfigurację.
|
||||||
|
|
||||||
|
## Struktura wyjścia
|
||||||
|
|
||||||
|
```
|
||||||
|
pages/
|
||||||
|
├── opinions-photos/
|
||||||
|
│ └── config.json
|
||||||
|
├── homepage/
|
||||||
|
│ └── config.json
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
24
docs/SETUP.md
Normal file
24
docs/SETUP.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Composer Builder Configuration
|
||||||
|
|
||||||
|
Dodaj tutaj swoje dane do konfiguracji.
|
||||||
|
|
||||||
|
## Jak znaleźć AUTH_TOKEN
|
||||||
|
|
||||||
|
1. Otwórz DevTools (F12) w przeglądarce
|
||||||
|
2. Idź do zakładki "Application" → "Cookies"
|
||||||
|
3. Szukaj cookie'a o nazwie `monit_token`
|
||||||
|
4. Skopiuj jego wartość
|
||||||
|
|
||||||
|
## Jak znaleźć TEMPLATE_ID
|
||||||
|
|
||||||
|
1. Otwórz URL szablonu w przeglądarce
|
||||||
|
2. URL wygląda podobnie do: `https://composer.idosell.com/compositions/69773895f35fc9.84872857`
|
||||||
|
3. Ostatnia część (po `.`) to TEMPLATE_ID
|
||||||
|
|
||||||
|
## Plik .env
|
||||||
|
|
||||||
|
```env
|
||||||
|
AUTH_TOKEN=30fa9d107ac0dbd46280e52d9039f0545364bf5a
|
||||||
|
TEMPLATE_ID=84872857
|
||||||
|
API_URL=https://composer.idosell.com/api
|
||||||
|
```
|
||||||
8
example.env
Normal file
8
example.env
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
AUTH_TOKEN=
|
||||||
|
TEMPLATE_ID=
|
||||||
|
API_URL=https://composer.idosell.com/api
|
||||||
|
|
||||||
|
# Cookies (zaktualizuj gdy ciasteczka wygasną)
|
||||||
|
PHPSESSID=
|
||||||
|
IAI_SYSTEM_CLIENT=
|
||||||
|
IAI_DASHBOARD=
|
||||||
1880
najnowszyrequest.txt
Normal file
1880
najnowszyrequest.txt
Normal file
File diff suppressed because it is too large
Load Diff
2799
package-lock.json
generated
Normal file
2799
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
35
package.json
Normal file
35
package.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "composer-builder",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Template page builder for iDoSell Composer",
|
||||||
|
"main": "src/index.js",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"setup": "node setup.js",
|
||||||
|
"template:pull": "node src/index.js",
|
||||||
|
"template:config": "node src/config.js",
|
||||||
|
"format": "prettier --write \"pages/**/*.{xml,less,js,json}\" \"src/**/*.js\"",
|
||||||
|
"format:check": "prettier --check \"pages/**/*.{xml,less,js,json}\" \"src/**/*.js\"",
|
||||||
|
"lint:less": "stylelint \"pages/**/*.less\""
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"composer",
|
||||||
|
"idosell",
|
||||||
|
"template"
|
||||||
|
],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.6.0",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
|
"fs-extra": "^11.1.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "^3.1.0",
|
||||||
|
"stylelint": "^15.11.0",
|
||||||
|
"stylelint-config-standard-less": "^1.0.0",
|
||||||
|
"stylelint-less": "^1.0.5",
|
||||||
|
"postcss": "^8.4.31",
|
||||||
|
"postcss-less": "^6.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
0
page request.md
Normal file
0
page request.md
Normal file
71
setup.js
Normal file
71
setup.js
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import readline from "readline";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
const envPath = path.join(__dirname, ".env");
|
||||||
|
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout,
|
||||||
|
});
|
||||||
|
|
||||||
|
function question(query) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
rl.question(query, resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.clear();
|
||||||
|
console.log("*** Setup Composer Builder ***\n");
|
||||||
|
console.log("Uzupełnij dane do konfiguracji API\n");
|
||||||
|
|
||||||
|
console.log("[INFO] Jak znaleźć dane:");
|
||||||
|
console.log("1. Zaloguj się do https://composer.idosell.com");
|
||||||
|
console.log("2. Otwórz DevTools (F12)");
|
||||||
|
console.log("3. Application → Cookies");
|
||||||
|
console.log("4. Skopiuj wartość cookie'a: monit_token\n");
|
||||||
|
|
||||||
|
const authToken = await question("[INPUT] AUTH_TOKEN (monit_token): ");
|
||||||
|
|
||||||
|
if (!authToken || authToken.trim().length < 10) {
|
||||||
|
console.log("\n[ERROR] Token jest zbyt krótki!");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const templateId = await question(
|
||||||
|
"[INPUT] TEMPLATE_ID (z URL: compositions/xxx.12345 → 12345): ",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!templateId || templateId.trim().length < 2) {
|
||||||
|
console.log("\n[ERROR] Template ID jest wymagany!");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiUrl =
|
||||||
|
(await question(
|
||||||
|
"[INPUT] API_URL (domyślnie: https://composer.idosell.com/api): ",
|
||||||
|
)) || "https://composer.idosell.com/api";
|
||||||
|
|
||||||
|
// Generuj .env
|
||||||
|
const envContent = `# Composer Builder Configuration
|
||||||
|
# Wygenerowano automatycznie
|
||||||
|
|
||||||
|
AUTH_TOKEN=${authToken.trim()}
|
||||||
|
TEMPLATE_ID=${templateId.trim()}
|
||||||
|
API_URL=${apiUrl.trim()}
|
||||||
|
`;
|
||||||
|
|
||||||
|
fs.writeFileSync(envPath, envContent);
|
||||||
|
|
||||||
|
console.log("\n[OK] Konfiguracja zapisana w .env");
|
||||||
|
console.log(`[INFO] Plik: ${envPath}\n`);
|
||||||
|
console.log("[NEXT] Możesz teraz uruchomić:");
|
||||||
|
console.log(" npm run template:pull\n");
|
||||||
|
|
||||||
|
rl.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error);
|
||||||
3
src/config-display.js
Normal file
3
src/config-display.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { displayConfig } from "./config.js";
|
||||||
|
|
||||||
|
displayConfig();
|
||||||
85
src/config.js
Normal file
85
src/config.js
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import dotenv from "dotenv";
|
||||||
|
import path from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
const envPath = path.join(__dirname, "..", ".env");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pobierz konfigurację ze zmiennych środowiska
|
||||||
|
*/
|
||||||
|
export function getConfig() {
|
||||||
|
return {
|
||||||
|
authToken: process.env.AUTH_TOKEN,
|
||||||
|
templateId: process.env.TEMPLATE_ID,
|
||||||
|
apiUrl: process.env.API_URL || "https://composer.idosell.com/api",
|
||||||
|
// Cookies
|
||||||
|
phpsessid: process.env.PHPSESSID || "",
|
||||||
|
iaiSystemClient: process.env.IAI_SYSTEM_CLIENT || "",
|
||||||
|
iaiDashboard: process.env.IAI_DASHBOARD || "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sprawdź, czy konfiguracja jest kompletna
|
||||||
|
*/
|
||||||
|
export function validateConfig() {
|
||||||
|
const config = getConfig();
|
||||||
|
|
||||||
|
if (!config.authToken) {
|
||||||
|
console.error("[ERROR] Brakuje AUTH_TOKEN w pliku .env");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.templateId) {
|
||||||
|
console.error("[ERROR] Brakuje TEMPLATE_ID w pliku .env");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("[OK] Konfiguracja załadowana poprawnie");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wyświetl obecną konfigurację
|
||||||
|
*/
|
||||||
|
export function displayConfig() {
|
||||||
|
const config = getConfig();
|
||||||
|
|
||||||
|
console.log("\n[CONFIG] Obecna konfiguracja:");
|
||||||
|
console.log("---".repeat(15));
|
||||||
|
console.log(`API URL: ${config.apiUrl}`);
|
||||||
|
console.log(
|
||||||
|
`Auth Token: ${config.authToken ? config.authToken.substring(0, 10) + "..." : "[ERROR] nie ustawiony"}`
|
||||||
|
);
|
||||||
|
console.log(`Template ID: ${config.templateId || "[ERROR] nie ustawiony"}`);
|
||||||
|
console.log("---".repeat(15));
|
||||||
|
|
||||||
|
if (!config.authToken || !config.templateId) {
|
||||||
|
console.log("\n[INFO] Edytuj plik .env aby ustawić konfigurację:");
|
||||||
|
console.log(`Plik: ${envPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zbuduj cookie string z konfiguracji
|
||||||
|
*/
|
||||||
|
export function buildCookieString(config) {
|
||||||
|
let cookies = `monit_token=${config.authToken}; lang=pl; currency_uk=eur; currency_pl=pln; type_of_visitor=client`;
|
||||||
|
|
||||||
|
if (config.phpsessid) {
|
||||||
|
cookies += `; PHPSESSID=${config.phpsessid}`;
|
||||||
|
}
|
||||||
|
if (config.iaiSystemClient) {
|
||||||
|
cookies += `; IAI_System_client=${config.iaiSystemClient}`;
|
||||||
|
}
|
||||||
|
if (config.iaiDashboard) {
|
||||||
|
cookies += `; iai_dashboard=${config.iaiDashboard}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cookies;
|
||||||
|
}
|
||||||
461
src/index.js
Normal file
461
src/index.js
Normal file
@@ -0,0 +1,461 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import fs from "fs-extra";
|
||||||
|
import path from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { getConfig, validateConfig, displayConfig, buildCookieString } from "./config.js";
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
const pagesDir = path.join(__dirname, "..", "pages");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sprawdź czy użytkownik jest zalogowany
|
||||||
|
*/
|
||||||
|
async function checkLogin(config) {
|
||||||
|
try {
|
||||||
|
console.log("[AUTH] Sprawdzanie zalogowania...");
|
||||||
|
|
||||||
|
const cookies = buildCookieString(config);
|
||||||
|
|
||||||
|
const response = await axios.get(`${config.apiUrl}/auth/get-logged-user/`, {
|
||||||
|
headers: {
|
||||||
|
Cookie: cookies,
|
||||||
|
"User-Agent":
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
|
||||||
|
Accept: "*/*",
|
||||||
|
"Accept-Language": "pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7",
|
||||||
|
"Cache-Control": "no-cache",
|
||||||
|
Pragma: "no-cache",
|
||||||
|
Referer:
|
||||||
|
`https://composer.idosell.com/compositions/${config.templateId}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data.response && response.data.response.id) {
|
||||||
|
console.log(`[OK] Zalogowany jako: ${response.data.response.name}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[ERROR] Błąd przy sprawdzaniu zalogowania:");
|
||||||
|
console.error(error.response?.data || error.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pobierz szczegóły strony (komponenty)
|
||||||
|
*/
|
||||||
|
async function fetchPageDetails(config, pageName) {
|
||||||
|
try {
|
||||||
|
const cookies = buildCookieString(config);
|
||||||
|
|
||||||
|
// TEMPLATE_ID jest w formacie: compositionId.version
|
||||||
|
const compositionId = config.templateId;
|
||||||
|
const url = `${config.apiUrl}/compositions/get-page/compositionId/${compositionId}/compositionVersion/1/page/${pageName}`;
|
||||||
|
|
||||||
|
console.log(` [URL] Pobieranie: ${url}`);
|
||||||
|
|
||||||
|
const response = await axios.get(url, {
|
||||||
|
headers: {
|
||||||
|
Cookie: cookies,
|
||||||
|
"User-Agent":
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
|
||||||
|
Accept: "*/*",
|
||||||
|
"Accept-Language": "pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7",
|
||||||
|
"Cache-Control": "no-cache",
|
||||||
|
Pragma: "no-cache",
|
||||||
|
Referer:
|
||||||
|
`https://composer.idosell.com/compositions/${compositionId}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data.response && response.data.response.page) {
|
||||||
|
return response.data.response.page;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
` ⚠️ Błąd przy pobieraniu szczegółów ${pageName}:`,
|
||||||
|
error.response?.status,
|
||||||
|
error.message,
|
||||||
|
);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pobierz strony z API
|
||||||
|
*/
|
||||||
|
async function fetchPages() {
|
||||||
|
const config = getConfig();
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log("[LOAD] Pobieranie listy stron...");
|
||||||
|
|
||||||
|
const cookies = buildCookieString(config);
|
||||||
|
|
||||||
|
const response = await axios.get(`${config.apiUrl}/pages/get`, {
|
||||||
|
headers: {
|
||||||
|
Cookie: cookies,
|
||||||
|
"User-Agent":
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
|
||||||
|
Accept: "*/*",
|
||||||
|
"Accept-Language": "pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7",
|
||||||
|
"Cache-Control": "no-cache",
|
||||||
|
Pragma: "no-cache",
|
||||||
|
Referer:
|
||||||
|
"https://composer.idosell.com/compositions/69773895f35fc9.84872857",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data.response && response.data.response.pages) {
|
||||||
|
console.log(`[OK] Pobrano ${response.data.response.pages.length} stron`);
|
||||||
|
return response.data.response.pages;
|
||||||
|
} else {
|
||||||
|
console.error("[ERROR] Nieoczekiwany format odpowiedzi API");
|
||||||
|
console.error("Odpowiedź:", JSON.stringify(response.data, null, 2));
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[ERROR] Błąd przy pobieraniu stron:");
|
||||||
|
console.error(error.response?.data || error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pobierz szczegóły komponentu ze najnowszą wersją
|
||||||
|
*/
|
||||||
|
async function fetchComponentDetails(config, componentId, componentVersion) {
|
||||||
|
try {
|
||||||
|
const cookies = buildCookieString(config);
|
||||||
|
|
||||||
|
// Użyj wersji komponenty z API, jeśli dostępna, w przeciwnym razie 1
|
||||||
|
const version = componentVersion || 1;
|
||||||
|
const url = `${config.apiUrl}/modules/get/id/${componentId}/version/${version}`;
|
||||||
|
|
||||||
|
const response = await axios.get(url, {
|
||||||
|
headers: {
|
||||||
|
Cookie: cookies,
|
||||||
|
"User-Agent":
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
|
||||||
|
Accept: "*/*",
|
||||||
|
"Accept-Language": "pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7",
|
||||||
|
"Cache-Control": "no-cache",
|
||||||
|
Pragma: "no-cache",
|
||||||
|
Referer:
|
||||||
|
`https://composer.idosell.com/modules/${componentId}/version/${version}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data && response.data.response) return response.data.response;
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
` [WARN] Błąd pobierania komponentu ${componentId} v${componentVersion}:`,
|
||||||
|
error.response?.status,
|
||||||
|
error.message,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createPageFolders(pages, config, onlyCustom = false) {
|
||||||
|
try {
|
||||||
|
// Wyczyść stary katalog jeśli istnieje
|
||||||
|
if (fs.existsSync(pagesDir)) {
|
||||||
|
console.log("[CLEAN] Czyszczenie starego katalogu pages/");
|
||||||
|
fs.removeSync(pagesDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stwórz nowy katalog
|
||||||
|
fs.ensureDirSync(pagesDir);
|
||||||
|
console.log(`[OK] Utworzono katalog: pages/\n`);
|
||||||
|
|
||||||
|
// Stwórz folder dla każdej strony
|
||||||
|
for (const page of pages) {
|
||||||
|
const pagePath = path.join(pagesDir, page.name);
|
||||||
|
fs.ensureDirSync(pagePath);
|
||||||
|
|
||||||
|
// Stwórz config.json dla każdej strony
|
||||||
|
const configFile = path.join(pagePath, "config.json");
|
||||||
|
const configContent = {
|
||||||
|
id: page.id,
|
||||||
|
name: page.name,
|
||||||
|
description: page.description,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
fs.writeFileSync(configFile, JSON.stringify(configContent, null, 2));
|
||||||
|
|
||||||
|
// Pobierz szczegóły strony (komponenty)
|
||||||
|
const pageDetails = await fetchPageDetails(config, page.name);
|
||||||
|
|
||||||
|
// Helper: utwórz folder komponentu (async, bo pobiera dane komponentu)
|
||||||
|
async function createComponentFolder(baseDir, component) {
|
||||||
|
const folderName = (component.group || component.name || "component")
|
||||||
|
.toString()
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, "_")
|
||||||
|
.replace(/[^a-z0-9_]/g, "");
|
||||||
|
|
||||||
|
const componentDir = path.join(baseDir, folderName);
|
||||||
|
fs.ensureDirSync(componentDir);
|
||||||
|
const componentFile = path.join(componentDir, "component.json");
|
||||||
|
fs.writeFileSync(componentFile, JSON.stringify(component, null, 2));
|
||||||
|
|
||||||
|
// Pobierz pełne dane komponentu i zapisz pliki ze sources
|
||||||
|
// Użyj wersji z API (component.version) zamiast zawsze 1
|
||||||
|
const version = component.version || component.version === 0 ? component.version : 1;
|
||||||
|
const details = await fetchComponentDetails(config, component.id, version);
|
||||||
|
if (details && details.sources) {
|
||||||
|
console.log(` [SOURCES] ${component.name} (v${version})`);
|
||||||
|
await writeSourceFiles(details.sources, componentDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper: zapisz pliki ze sources
|
||||||
|
async function writeSourceFiles(sources, componentDir) {
|
||||||
|
// Mapowanie klucz -> rozszerzenie
|
||||||
|
const sourceExtensions = {
|
||||||
|
javascript: "js",
|
||||||
|
jsLocal: "js",
|
||||||
|
css: "less",
|
||||||
|
xml: "xml",
|
||||||
|
xmlBefore: "xml",
|
||||||
|
xmlAfter: "xml",
|
||||||
|
attributes: "json",
|
||||||
|
translations: "json",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper do zapisu pliku (tworzy plik nawet jeśli pusty)
|
||||||
|
function writeFile(content, key) {
|
||||||
|
if (typeof content === "undefined" || content === null) return;
|
||||||
|
|
||||||
|
const ext = sourceExtensions[key] || "txt";
|
||||||
|
const filename = `${key}.${ext}`;
|
||||||
|
const filePath = path.join(componentDir, filename);
|
||||||
|
|
||||||
|
let contentToWrite = content;
|
||||||
|
if (typeof content === "object") {
|
||||||
|
contentToWrite = JSON.stringify(content, null, 2);
|
||||||
|
} else if (typeof content !== "string") {
|
||||||
|
contentToWrite = String(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(filePath, contentToWrite, "utf8");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zmapuj wszystkie klucze sources na pliki
|
||||||
|
Object.keys(sources).forEach((key) => {
|
||||||
|
if (key === "attributes" || key === "translations") {
|
||||||
|
// te już obsługujemy osobno
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
writeFile(sources[key], key);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Osobnie attributes i translations jeśli są i są obiektami
|
||||||
|
if (sources.attributes && typeof sources.attributes === "object") {
|
||||||
|
writeFile(sources.attributes, "attributes");
|
||||||
|
}
|
||||||
|
if (sources.translations && typeof sources.translations === "object") {
|
||||||
|
writeFile(sources.translations, "translations");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rekursywnie szukaj componentów z type: custom i twórz foldery
|
||||||
|
async function processItems(items, baseDir, onlyCustom = false) {
|
||||||
|
if (!items) return;
|
||||||
|
if (!Array.isArray(items)) return;
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
// Jeśli onlyCustom = false, tworzymy folder dla każdego komponentu
|
||||||
|
// Jeśli onlyCustom = true, tworzymy tylko dla type: custom
|
||||||
|
if (!onlyCustom || item.type === "custom") {
|
||||||
|
await createComponentFolder(baseDir, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// jeśli element ma wewnętrzne components, rekurencyjnie je przetwórz
|
||||||
|
if (item.components && Array.isArray(item.components)) {
|
||||||
|
await processItems(item.components, baseDir, onlyCustom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageDetails) {
|
||||||
|
// Jeśli API zwróciło obiekt z kluczem components -> traktuj jako wrapper
|
||||||
|
if (
|
||||||
|
!Array.isArray(pageDetails) &&
|
||||||
|
pageDetails.components &&
|
||||||
|
Array.isArray(pageDetails.components)
|
||||||
|
) {
|
||||||
|
// użyj group lub name jako nazwy wrappera (sanitizowane)
|
||||||
|
const rawWrapperName = (
|
||||||
|
pageDetails.group ||
|
||||||
|
pageDetails.name ||
|
||||||
|
"wrapper"
|
||||||
|
).toString();
|
||||||
|
const wrapperName = rawWrapperName
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, "_")
|
||||||
|
.replace(/[^a-z0-9_]/g, "");
|
||||||
|
|
||||||
|
const wrapperDir = path.join(pagePath, wrapperName);
|
||||||
|
fs.ensureDirSync(wrapperDir);
|
||||||
|
|
||||||
|
// Zapisz component.json dla wrappera
|
||||||
|
const wrapperComponentFile = path.join(wrapperDir, "component.json");
|
||||||
|
fs.writeFileSync(
|
||||||
|
wrapperComponentFile,
|
||||||
|
JSON.stringify(pageDetails, null, 2),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Zapisz pliki ze sources dla wrappera
|
||||||
|
if (pageDetails.sources) {
|
||||||
|
await writeSourceFiles(pageDetails.sources, wrapperDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
await processItems(pageDetails.components, wrapperDir, onlyCustom);
|
||||||
|
} else if (Array.isArray(pageDetails)) {
|
||||||
|
// Jeśli otrzymaliśmy tablicę, najpierw znajdź wrappery (model === 'wrapper')
|
||||||
|
// znajdź potencjalne wrappery (mają model 'wrapper' lub components[])
|
||||||
|
const possibleWrappers = pageDetails.filter(
|
||||||
|
(it) =>
|
||||||
|
it &&
|
||||||
|
(it.model === "wrapper" ||
|
||||||
|
(it.components && Array.isArray(it.components))),
|
||||||
|
);
|
||||||
|
|
||||||
|
// helper: sprawdź rekursywnie czy w items jest jakikolwiek custom
|
||||||
|
function hasCustom(items) {
|
||||||
|
if (!items) return false;
|
||||||
|
if (!Array.isArray(items)) return false;
|
||||||
|
for (const it of items) {
|
||||||
|
if (!it) continue;
|
||||||
|
if (it.type === "custom") return true;
|
||||||
|
if (it.components && Array.isArray(it.components)) {
|
||||||
|
if (hasCustom(it.components)) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const processedWrapperIds = new Set();
|
||||||
|
|
||||||
|
for (const wrapper of possibleWrappers) {
|
||||||
|
// tylko twórz wrapper jeśli wewnątrz są custom komponenty
|
||||||
|
if (!hasCustom(wrapper.components)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawWrapperName = (
|
||||||
|
wrapper.group ||
|
||||||
|
wrapper.name ||
|
||||||
|
"wrapper"
|
||||||
|
).toString();
|
||||||
|
const wrapperName = rawWrapperName
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, "_")
|
||||||
|
.replace(/[^a-z0-9_]/g, "");
|
||||||
|
|
||||||
|
const wrapperDir = path.join(pagePath, wrapperName);
|
||||||
|
fs.ensureDirSync(wrapperDir);
|
||||||
|
|
||||||
|
// Zapisz component.json dla wrappera
|
||||||
|
const wrapperComponentFile = path.join(
|
||||||
|
wrapperDir,
|
||||||
|
"component.json",
|
||||||
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
wrapperComponentFile,
|
||||||
|
JSON.stringify(wrapper, null, 2),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Zapisz pliki ze sources dla wrappera
|
||||||
|
if (wrapper.sources) {
|
||||||
|
await writeSourceFiles(wrapper.sources, wrapperDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(wrapper.components)) {
|
||||||
|
await processItems(wrapper.components, wrapperDir, onlyCustom);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrapper.id) processedWrapperIds.add(wrapper.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Przetwórz pozostałe elementy (pomijając wrappery)
|
||||||
|
const remaining = pageDetails.filter(
|
||||||
|
(it) => !processedWrapperIds.has(it.id),
|
||||||
|
);
|
||||||
|
await processItems(remaining, pagePath, onlyCustom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Policz rzeczywiste komponenty w folderze strony
|
||||||
|
const componentDirs = fs.readdirSync(pagePath).filter((f) => {
|
||||||
|
const fullPath = path.join(pagePath, f);
|
||||||
|
return fs.statSync(fullPath).isDirectory();
|
||||||
|
});
|
||||||
|
const customText =
|
||||||
|
componentDirs.length > 0 ? ` (${componentDirs.length})` : "";
|
||||||
|
console.log(` [OK] ${page.name}${customText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\n[DONE] Ukończono! Przetworzono ${pages.length} stron`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[ERROR] Błąd przy tworzeniu folderów:");
|
||||||
|
console.error(error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Główna funkcja
|
||||||
|
*/
|
||||||
|
async function main() {
|
||||||
|
console.clear();
|
||||||
|
console.log("*** Composer Builder - Template Puller ***\n");
|
||||||
|
|
||||||
|
// Czytaj flagi z argumentów
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
const onlyCustom = args.includes("--custom");
|
||||||
|
|
||||||
|
// Wyświetl tryb
|
||||||
|
if (onlyCustom) {
|
||||||
|
console.log("[MODE] Pobieranie TYLKO komponentów custom\n");
|
||||||
|
} else {
|
||||||
|
console.log("[MODE] Pobieranie WSZYSTKICH komponentów\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wyświetl konfigurację
|
||||||
|
displayConfig();
|
||||||
|
|
||||||
|
// Walidacja konfiguracji
|
||||||
|
if (!validateConfig()) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = getConfig();
|
||||||
|
|
||||||
|
// Sprawdź zalogowanie
|
||||||
|
const isLogged = await checkLogin(config);
|
||||||
|
if (!isLogged) {
|
||||||
|
console.log("\n[ERROR] Nie jesteś zalogowany. Token może być wygaszony.");
|
||||||
|
console.log("[INFO] Zaktualizuj AUTH_TOKEN w pliku .env");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pobierz strony
|
||||||
|
const pages = await fetchPages();
|
||||||
|
|
||||||
|
if (pages.length === 0) {
|
||||||
|
console.log("[WARN] Brak stron do pobrania");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stwórz foldery
|
||||||
|
await createPageFolders(pages, config, onlyCustom);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error);
|
||||||
Reference in New Issue
Block a user