├── .env.example ├── .gitignore ├── DownloadAndUploadImages.js ├── README.md ├── config ├── admin.js ├── api.js ├── database.js ├── middlewares.js ├── plugins.js └── server.js ├── data ├── data.json └── uploads │ ├── a-bug-is-becoming-a-meme-on-the-internet.jpg │ ├── beautiful-picture.jpg │ ├── coffee-art.jpg │ ├── coffee-beans.jpg │ ├── coffee-shadow.jpg │ ├── daviddoe@strapi.io.jpg │ ├── default-image.png │ ├── favicon.png │ ├── sarahbaker@strapi.io.jpg │ ├── the-internet-s-own-boy.jpg │ ├── this-shrimp-is-awesome.jpg │ ├── we-love-pizza.jpg │ └── what-s-inside-a-black-hole.jpg ├── database └── migrations │ └── .gitkeep ├── favicon.png ├── jsconfig.json ├── package.json ├── public ├── robots.txt └── uploads │ └── .gitkeep ├── scripts └── seed.js ├── src ├── admin │ ├── app.example.js │ └── vite.config.example.js ├── api │ ├── .gitkeep │ ├── about │ │ ├── content-types │ │ │ └── about │ │ │ │ └── schema.json │ │ ├── controllers │ │ │ └── about.js │ │ ├── routes │ │ │ └── about.js │ │ └── services │ │ │ └── about.js │ ├── article │ │ ├── content-types │ │ │ └── article │ │ │ │ └── schema.json │ │ ├── controllers │ │ │ └── article.js │ │ ├── routes │ │ │ └── article.js │ │ └── services │ │ │ └── article.js │ ├── author │ │ ├── content-types │ │ │ └── author │ │ │ │ └── schema.json │ │ ├── controllers │ │ │ └── author.js │ │ ├── routes │ │ │ └── author.js │ │ └── services │ │ │ └── author.js │ ├── category │ │ ├── content-types │ │ │ └── category │ │ │ │ └── schema.json │ │ ├── controllers │ │ │ └── category.js │ │ ├── routes │ │ │ └── category.js │ │ └── services │ │ │ └── category.js │ ├── global │ │ ├── content-types │ │ │ └── global │ │ │ │ └── schema.json │ │ ├── controllers │ │ │ └── global.js │ │ ├── routes │ │ │ └── global.js │ │ └── services │ │ │ └── global.js │ └── post │ │ ├── content-types │ │ └── post │ │ │ └── schema.json │ │ ├── controllers │ │ └── post.js │ │ ├── routes │ │ └── post.js │ │ └── services │ │ └── post.js ├── components │ └── shared │ │ ├── media.json │ │ ├── quote.json │ │ ├── rich-text.json │ │ ├── seo.json │ │ └── slider.json ├── extensions │ └── .gitkeep └── index.js ├── types └── generated │ ├── components.d.ts │ └── contentTypes.d.ts ├── upload_posts.js └── yarn.lock /.env.example: -------------------------------------------------------------------------------- 1 | HOST=0.0.0.0 2 | PORT=1337 3 | APP_KEYS="toBeModified1,toBeModified2" 4 | API_TOKEN_SALT=tobemodified 5 | ADMIN_JWT_SECRET=tobemodified 6 | TRANSFER_TOKEN_SALT=tobemodified 7 | JWT_SECRET=tobemodified 8 | 9 | #script 10 | STRAPI_UPLOAD_URL= "http://localhost:1337/api/upload"; 11 | STRAPI_TOKEN= "f4eb45**********************************368da2b1a49"; // Replace with your actual token 12 | WORDPRESS_API= "http://your_wordpress_api/wp-json/custom/v1/posts"; 13 | STRAPI_URL= "***********" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ############################ 2 | # OS X 3 | ############################ 4 | 5 | .DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | Icon 9 | .Spotlight-V100 10 | .Trashes 11 | ._* 12 | 13 | 14 | ############################ 15 | # Linux 16 | ############################ 17 | 18 | *~ 19 | 20 | 21 | ############################ 22 | # Windows 23 | ############################ 24 | 25 | Thumbs.db 26 | ehthumbs.db 27 | Desktop.ini 28 | $RECYCLE.BIN/ 29 | *.cab 30 | *.msi 31 | *.msm 32 | *.msp 33 | 34 | 35 | ############################ 36 | # Packages 37 | ############################ 38 | 39 | *.7z 40 | *.csv 41 | *.dat 42 | *.dmg 43 | *.gz 44 | *.iso 45 | *.jar 46 | *.rar 47 | *.tar 48 | *.zip 49 | *.com 50 | *.class 51 | *.dll 52 | *.exe 53 | *.o 54 | *.seed 55 | *.so 56 | *.swo 57 | *.swp 58 | *.swn 59 | *.swm 60 | *.out 61 | *.pid 62 | 63 | 64 | ############################ 65 | # Logs and databases 66 | ############################ 67 | 68 | .tmp 69 | *.log 70 | *.sql 71 | *.sqlite 72 | *.sqlite3 73 | 74 | 75 | ############################ 76 | # Misc. 77 | ############################ 78 | 79 | *# 80 | ssl 81 | .idea 82 | nbproject 83 | public/uploads/* 84 | !public/uploads/.gitkeep 85 | .tsbuildinfo 86 | .eslintcache 87 | 88 | ############################ 89 | # Node.js 90 | ############################ 91 | 92 | lib-cov 93 | lcov.info 94 | pids 95 | logs 96 | results 97 | node_modules 98 | .node_history 99 | 100 | ############################ 101 | # Package managers 102 | ############################ 103 | 104 | .yarn/* 105 | !.yarn/cache 106 | !.yarn/unplugged 107 | !.yarn/patches 108 | !.yarn/releases 109 | !.yarn/sdks 110 | !.yarn/versions 111 | .pnp.* 112 | yarn-error.log 113 | 114 | ############################ 115 | # Tests 116 | ############################ 117 | 118 | coverage 119 | 120 | ############################ 121 | # Strapi 122 | ############################ 123 | 124 | .env 125 | license.txt 126 | exports 127 | .strapi 128 | dist 129 | build 130 | .strapi-updater.json 131 | .strapi-cloud.json -------------------------------------------------------------------------------- /DownloadAndUploadImages.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const axios = require("axios"); 4 | const FormData = require("form-data"); 5 | const dotenv = require('dotenv'); 6 | dotenv.config(); 7 | 8 | const STRAPI_UPLOAD_URL = process.env.STRAPI_UPLOAD_URL; 9 | const STRAPI_TOKEN = process.env.STRAPI_TOKEN; 10 | const WORDPRESS_API = process.env.WORDPRESS_API; 11 | const TEMP_MEDIA_DIR = path.join(__dirname, "temp_media"); 12 | const IMAGE_MAPPING_FILE = path.join(__dirname, "image_mappings.json"); 13 | 14 | const DELAY_MS = 5000; 15 | const MAX_RETRIES = 3; 16 | 17 | let imageMappings = {}; 18 | if (fs.existsSync(IMAGE_MAPPING_FILE)) { 19 | imageMappings = JSON.parse(fs.readFileSync(IMAGE_MAPPING_FILE, "utf8")); 20 | } 21 | 22 | // Ensure temp_media directory exists 23 | if (!fs.existsSync(TEMP_MEDIA_DIR)) { 24 | fs.mkdirSync(TEMP_MEDIA_DIR, { recursive: true }); 25 | } 26 | 27 | // Download image from Wordpress Api 28 | async function downloadImage(imageUrl, filename) { 29 | try { 30 | console.log(`📥 Downloading image: ${imageUrl}...`); 31 | const imagePath = path.join(TEMP_MEDIA_DIR, filename); 32 | 33 | const response = await axios({ 34 | url: imageUrl, 35 | responseType: "stream", 36 | }); 37 | 38 | await new Promise((resolve, reject) => { 39 | const stream = response.data.pipe(fs.createWriteStream(imagePath)); 40 | stream.on("finish", resolve); 41 | stream.on("error", reject); 42 | }); 43 | 44 | console.log(`✅ Image saved: ${imagePath}`); 45 | return imagePath; 46 | } catch (error) { 47 | console.error(`❌ Error downloading image: ${imageUrl}`, error.message); 48 | return null; 49 | } 50 | } 51 | 52 | // Upload image to Strapi 53 | async function uploadImage(filePath, attempt = 1) { 54 | const fileName = path.basename(filePath); 55 | 56 | if (imageMappings[fileName]) { 57 | console.log(`✅ Image already uploaded: ${fileName}`); 58 | return imageMappings[fileName]; 59 | } 60 | 61 | try { 62 | console.log(`📤 Uploading: ${filePath} (Attempt ${attempt})`); 63 | 64 | const formData = new FormData(); 65 | formData.append("files", fs.createReadStream(filePath)); 66 | 67 | const response = await axios.post(STRAPI_UPLOAD_URL, formData, { 68 | headers: { 69 | Authorization: `Bearer ${STRAPI_TOKEN}`, 70 | ...formData.getHeaders(), 71 | }, 72 | }); 73 | 74 | const uploadedImage = response.data[0]; 75 | console.log(`✅ Uploaded: ${uploadedImage.url}`); 76 | 77 | // Save image mapping 78 | imageMappings[fileName] = uploadedImage.id; 79 | fs.writeFileSync(IMAGE_MAPPING_FILE, JSON.stringify(imageMappings, null, 2)); 80 | 81 | return uploadedImage.id; 82 | } catch (error) { 83 | console.error(`❌ Error uploading ${fileName}: ${error.message}`); 84 | 85 | if (attempt < MAX_RETRIES) { 86 | console.log(`🔄 Retrying ${fileName} (Attempt ${attempt + 1}) in ${DELAY_MS}ms...`); 87 | await new Promise(resolve => setTimeout(resolve, DELAY_MS)); 88 | return uploadImage(filePath, attempt + 1); 89 | } else { 90 | console.error(`❌ Skipping ${fileName} after ${MAX_RETRIES} failed attempts.`); 91 | return null; 92 | } 93 | } 94 | } 95 | 96 | // Fetch WordPress posts 97 | async function fetchWordPressPosts() { 98 | try { 99 | console.log("📡 Fetching posts from WordPress API..."); 100 | const response = await axios.get(WORDPRESS_API); 101 | console.log("✅ Data fetched:", response.data.length, "posts found."); 102 | return response.data; 103 | } catch (error) { 104 | console.error("❌ Error fetching WordPress posts:", error.message); 105 | process.exit(1); 106 | } 107 | } 108 | 109 | // Process images from WordPress posts 110 | async function processImages() { 111 | const posts = await fetchWordPressPosts(); 112 | 113 | for (const post of posts) { 114 | if (post.featured_image) { 115 | const imageUrl = post.featured_image; 116 | const fileName = path.basename(imageUrl); 117 | 118 | const imagePath = await downloadImage(imageUrl, fileName); 119 | if (imagePath) { 120 | await uploadImage(imagePath); 121 | await new Promise(resolve => setTimeout(resolve, DELAY_MS)); 122 | } 123 | } 124 | } 125 | console.log("🎉 Image processing completed!"); 126 | } 127 | 128 | processImages(); 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WordPress to Strapi Migration 2 | 3 | This guide outlines the step-by-step process for migrating WordPress posts and media assets to Strapi using two scripts: 4 | 5 | 1. **DownloadAndUploadImages.js** – Downloads images from WordPress and uploads them to Strapi. 6 | 2. **upload_posts.js** – Fetches posts from WordPress and imports them into Strapi, linking the uploaded images. 7 | 8 | ## Prerequisites 9 | 10 | Before running the scripts, ensure you have: 11 | 12 | - **Node.js** installed. 13 | - **Strapi** instance running (default: `http://localhost:1337`). 14 | - **WordPress API** endpoint accessible. 15 | - **Valid Strapi API Token** for authentication. 16 | 17 | ## Step 1: Configure the Scripts 18 | 19 | Before running the scripts, update the following variables: 20 | 21 | - **Strapi API Token** (`STRAPI_TOKEN`) in both `DownloadAndUploadImages.js` and `upload_posts.js`. 22 | - **Strapi Upload URL** (`STRAPI_UPLOAD_URL`) in `DownloadAndUploadImages.js`. 23 | - **WordPress API URL** (`WORDPRESS_API`) in `upload_posts.js`. 24 | 25 | ## Step 2: Install Dependencies 26 | 27 | Navigate to the project directory and install the required dependencies: 28 | 29 | ```bash 30 | npm install axios form-data date-fns 31 | ``` 32 | 33 | ## Step 3: Upload Media Assets 34 | 35 | Run the `DownloadAndUploadImages.js` script to upload all media assets from WordPress to Strapi: 36 | 37 | ```bash 38 | node DownloadAndUploadImages.js 39 | ``` 40 | 41 | ### Important Notes: 42 | - The script reads images from the `temp_media` folder. 43 | - If an image has already been uploaded, it will be skipped. 44 | - If you want to re-upload all media assets, delete `image_mappings.json` before running the script. 45 | 46 | ## Step 4: Migrate Posts 47 | 48 | After uploading images, run the `upload_posts.js` script to migrate WordPress posts to Strapi: 49 | 50 | ```bash 51 | node upload_posts.js 52 | ``` 53 | 54 | ### Features: 55 | - Fetches posts from the WordPress API. 56 | - Checks if the featured image is uploaded and assigns the correct reference. 57 | - Skips posts that already exist in Strapi (based on slug uniqueness). 58 | - Ensures a smooth migration process with error handling and retry mechanisms. 59 | 60 | ## Troubleshooting 61 | 62 | - **Image upload fails repeatedly?** Check if Strapi’s media library is configured correctly. 63 | - **Posts are not saving?** Ensure the `slug` field in Strapi is set as a **unique identifier (UID)**. 64 | - **Authentication errors?** Verify that the Strapi API token has the correct permissions. 65 | 66 | ## Blog 67 | 68 | You can also check our blog for a detailed step-by-step migration guide: [How to Migrate from WordPress to Strapi](https://www.rwit.io/blog/migrating-wordpress-to-strapi) 69 | 70 | ## Acknowledgments 71 | 72 | A [remote agency](https://www.rwit.io/) with a global reach specializing in developing custom software and headless applications 73 | 74 | Made with Love by [RWIT](https://www.rwit.io/) 75 | 76 | ## Contact Us 77 | 78 | For any questions or support, feel free to reach out at [RWIT](https://www.rwit.io/contact?utm_source=www&utm_medium=contactbutton&utm_campaign=visit). 79 | 80 | 81 | ## Conclusion 82 | 83 | By following this process, you can efficiently migrate your WordPress content to Strapi while preserving media relationships and ensuring data integrity. 84 | 85 | ## License 86 | This script is open-source and can be modified as needed for your migration requirements. 87 | 88 | -------------------------------------------------------------------------------- /config/admin.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ env }) => ({ 2 | auth: { 3 | secret: env('ADMIN_JWT_SECRET'), 4 | }, 5 | apiToken: { 6 | salt: env('API_TOKEN_SALT'), 7 | }, 8 | transfer: { 9 | token: { 10 | salt: env('TRANSFER_TOKEN_SALT'), 11 | }, 12 | }, 13 | flags: { 14 | nps: env.bool('FLAG_NPS', true), 15 | promoteEE: env.bool('FLAG_PROMOTE_EE', true), 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /config/api.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rest: { 3 | defaultLimit: 25, 4 | maxLimit: 100, 5 | withCount: true, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /config/database.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = ({ env }) => { 4 | const client = env('DATABASE_CLIENT', 'sqlite'); 5 | 6 | const connections = { 7 | mysql: { 8 | connection: { 9 | host: env('DATABASE_HOST', 'localhost'), 10 | port: env.int('DATABASE_PORT', 3306), 11 | database: env('DATABASE_NAME', 'strapi'), 12 | user: env('DATABASE_USERNAME', 'strapi'), 13 | password: env('DATABASE_PASSWORD', 'strapi'), 14 | ssl: env.bool('DATABASE_SSL', false) && { 15 | key: env('DATABASE_SSL_KEY', undefined), 16 | cert: env('DATABASE_SSL_CERT', undefined), 17 | ca: env('DATABASE_SSL_CA', undefined), 18 | capath: env('DATABASE_SSL_CAPATH', undefined), 19 | cipher: env('DATABASE_SSL_CIPHER', undefined), 20 | rejectUnauthorized: env.bool('DATABASE_SSL_REJECT_UNAUTHORIZED', true), 21 | }, 22 | }, 23 | pool: { min: env.int('DATABASE_POOL_MIN', 2), max: env.int('DATABASE_POOL_MAX', 10) }, 24 | }, 25 | postgres: { 26 | connection: { 27 | connectionString: env('DATABASE_URL'), 28 | host: env('DATABASE_HOST', 'localhost'), 29 | port: env.int('DATABASE_PORT', 5432), 30 | database: env('DATABASE_NAME', 'strapi'), 31 | user: env('DATABASE_USERNAME', 'strapi'), 32 | password: env('DATABASE_PASSWORD', 'strapi'), 33 | ssl: env.bool('DATABASE_SSL', false) && { 34 | key: env('DATABASE_SSL_KEY', undefined), 35 | cert: env('DATABASE_SSL_CERT', undefined), 36 | ca: env('DATABASE_SSL_CA', undefined), 37 | capath: env('DATABASE_SSL_CAPATH', undefined), 38 | cipher: env('DATABASE_SSL_CIPHER', undefined), 39 | rejectUnauthorized: env.bool('DATABASE_SSL_REJECT_UNAUTHORIZED', true), 40 | }, 41 | schema: env('DATABASE_SCHEMA', 'public'), 42 | }, 43 | pool: { min: env.int('DATABASE_POOL_MIN', 2), max: env.int('DATABASE_POOL_MAX', 10) }, 44 | }, 45 | sqlite: { 46 | connection: { 47 | filename: path.join(__dirname, '..', env('DATABASE_FILENAME', '.tmp/data.db')), 48 | }, 49 | useNullAsDefault: true, 50 | }, 51 | }; 52 | 53 | return { 54 | connection: { 55 | client, 56 | ...connections[client], 57 | acquireConnectionTimeout: env.int('DATABASE_CONNECTION_TIMEOUT', 60000), 58 | }, 59 | }; 60 | }; 61 | -------------------------------------------------------------------------------- /config/middlewares.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | 'strapi::logger', 3 | 'strapi::errors', 4 | 'strapi::security', 5 | 'strapi::cors', 6 | 'strapi::poweredBy', 7 | 'strapi::query', 8 | 'strapi::body', 9 | 'strapi::session', 10 | 'strapi::favicon', 11 | 'strapi::public', 12 | ]; 13 | -------------------------------------------------------------------------------- /config/plugins.js: -------------------------------------------------------------------------------- 1 | module.exports = () => ({}); 2 | -------------------------------------------------------------------------------- /config/server.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ env }) => ({ 2 | host: env('HOST', '0.0.0.0'), 3 | port: env.int('PORT', 1337), 4 | app: { 5 | keys: env.array('APP_KEYS'), 6 | }, 7 | webhooks: { 8 | populateRelations: env.bool('WEBHOOKS_POPULATE_RELATIONS', false), 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /data/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "global": { 3 | "siteName": "Strapi Blog", 4 | "defaultSeo": { 5 | "metaTitle": "Page", 6 | "metaDescription": "A blog made with Strapi", 7 | "shareImage": null 8 | }, 9 | "siteDescription": "A Blog made with Strapi", 10 | "favicon": null 11 | }, 12 | "about": { 13 | "title": "About the strapi blog", 14 | "blocks": [ 15 | { 16 | "__component": "shared.quote", 17 | "title": "Thelonius Monk", 18 | "body": "You've got to dig it to dig it, you dig?" 19 | }, 20 | { 21 | "__component": "shared.rich-text", 22 | "body": "## Dedit imago conspicuus cum capillis totidem inhibere\n\nLorem markdownum **rerum**, est limine: columbas: ab infelix hostem arbore nudis\ncrudelis. Videtur reliquit ambo ferrum dote sub amne fatis **illuc**, in magis,\nnec." 23 | }, 24 | { 25 | "__component": "shared.media", 26 | "file": "coffee-art.jpg" 27 | } 28 | ] 29 | }, 30 | "categories": [ 31 | { 32 | "name": "news", 33 | "slug": "news" 34 | }, 35 | { 36 | "name": "tech", 37 | "slug": "tech" 38 | }, 39 | { 40 | "name": "food", 41 | "slug": "food" 42 | }, 43 | { 44 | "name": "nature", 45 | "slug": "nature" 46 | }, 47 | { 48 | "name": "story", 49 | "slug": "story" 50 | } 51 | ], 52 | "authors": [ 53 | { 54 | "name": "David Doe", 55 | "email": "daviddoe@strapi.io", 56 | "avatar": "daviddoe@strapi.io.jpg" 57 | }, 58 | { 59 | "name": "Sarah Baker", 60 | "email": "sarahbaker@strapi.io", 61 | "avatar": "sarahbaker@strapi.io.jpg" 62 | } 63 | ], 64 | "articles": [ 65 | { 66 | "title": "The internet's Own boy", 67 | "slug": "the-internet-s-own-boy", 68 | "category": { 69 | "id": 5 70 | }, 71 | "author": { 72 | "id": 1 73 | }, 74 | "description": "Follow the story of Aaron Swartz, the boy who could change the world", 75 | "cover": null, 76 | "blocks": [ 77 | { 78 | "__component": "shared.rich-text", 79 | "body": "## Probant \n\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. \n\n## Abit sua\n\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. " 80 | }, 81 | { 82 | "__component": "shared.quote", 83 | "title": "Thelonius Monk", 84 | "body": "You've got to dig it to dig it, you dig?" 85 | }, 86 | { 87 | "__component": "shared.media", 88 | "file": "coffee-art.jpg" 89 | }, 90 | { 91 | "__component": "shared.rich-text", 92 | "body": "## Spatiantia astra \n\nFoeda, medio silva *errandum*: onus formam munere. Mutata bibulis est auxiliare arces etiamnunc verbis virgineo Priamidas illa Thescelus, nam fit locis lucis auras. Exitus hospes gratulor ut pondere [speslimite](http://www.curas.io/figuram); quid habent, Avernales faciente de. Pervenit Ino sonabile supplex cognoscenti vires, Bacchumque errat miserarum venandi dignabere dedisti. Discrimina iuncosaque virgaque tot sine superest [fissus](http://quos.org/sitet.aspx). Non color esset potest non sumit, sed vix arserat. Nisi immo silva tantum pectusque quos pennis quisquam artus!" 93 | }, 94 | { 95 | "__component": "shared.slider", 96 | "files": ["coffee-art.jpg", "coffee-beans.jpg"] 97 | } 98 | ] 99 | }, 100 | { 101 | "title": "This shrimp is awesome", 102 | "slug": "this-shrimp-is-awesome", 103 | "category": { 104 | "id": 4 105 | }, 106 | "author": { 107 | "id": 1 108 | }, 109 | "description": "Mantis shrimps, or stomatopods, are marine crustaceans of the order Stomatopoda.", 110 | "cover": null, 111 | "blocks": [ 112 | { 113 | "__component": "shared.rich-text", 114 | "body": "## Probant \n\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. \n\n## Abit sua\n\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. " 115 | }, 116 | { 117 | "__component": "shared.quote", 118 | "title": "Thelonius Monk", 119 | "body": "You've got to dig it to dig it, you dig?" 120 | }, 121 | { 122 | "__component": "shared.media", 123 | "file": "coffee-art.jpg" 124 | }, 125 | { 126 | "__component": "shared.rich-text", 127 | "body": "## Spatiantia astra \n\nFoeda, medio silva *errandum*: onus formam munere. Mutata bibulis est auxiliare arces etiamnunc verbis virgineo Priamidas illa Thescelus, nam fit locis lucis auras. Exitus hospes gratulor ut pondere [speslimite](http://www.curas.io/figuram); quid habent, Avernales faciente de. Pervenit Ino sonabile supplex cognoscenti vires, Bacchumque errat miserarum venandi dignabere dedisti. Discrimina iuncosaque virgaque tot sine superest [fissus](http://quos.org/sitet.aspx). Non color esset potest non sumit, sed vix arserat. Nisi immo silva tantum pectusque quos pennis quisquam artus!" 128 | }, 129 | { 130 | "__component": "shared.slider", 131 | "files": ["coffee-art.jpg", "coffee-beans.jpg"] 132 | } 133 | ] 134 | }, 135 | { 136 | "title": "A bug is becoming a meme on the internet", 137 | "slug": "a-bug-is-becoming-a-meme-on-the-internet", 138 | "category": { 139 | "id": 2 140 | }, 141 | "author": { 142 | "id": 2 143 | }, 144 | "description": "How a bug on MySQL is becoming a meme on the internet", 145 | "cover": null, 146 | "blocks": [ 147 | { 148 | "__component": "shared.rich-text", 149 | "body": "## Probant \n\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. \n\n## Abit sua\n\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. " 150 | }, 151 | { 152 | "__component": "shared.quote", 153 | "title": "Thelonius Monk", 154 | "body": "You've got to dig it to dig it, you dig?" 155 | }, 156 | { 157 | "__component": "shared.media", 158 | "file": "coffee-art.jpg" 159 | }, 160 | { 161 | "__component": "shared.rich-text", 162 | "body": "## Spatiantia astra \n\nFoeda, medio silva *errandum*: onus formam munere. Mutata bibulis est auxiliare arces etiamnunc verbis virgineo Priamidas illa Thescelus, nam fit locis lucis auras. Exitus hospes gratulor ut pondere [speslimite](http://www.curas.io/figuram); quid habent, Avernales faciente de. Pervenit Ino sonabile supplex cognoscenti vires, Bacchumque errat miserarum venandi dignabere dedisti. Discrimina iuncosaque virgaque tot sine superest [fissus](http://quos.org/sitet.aspx). Non color esset potest non sumit, sed vix arserat. Nisi immo silva tantum pectusque quos pennis quisquam artus!" 163 | }, 164 | { 165 | "__component": "shared.slider", 166 | "files": ["coffee-art.jpg", "coffee-beans.jpg"] 167 | } 168 | ] 169 | }, 170 | { 171 | "title": "Beautiful picture", 172 | "slug": "beautiful-picture", 173 | "category": { 174 | "id": 4 175 | }, 176 | "author": { 177 | "id": 2 178 | }, 179 | "description": "Description of a beautiful picture", 180 | "cover": null, 181 | "blocks": [ 182 | { 183 | "__component": "shared.rich-text", 184 | "body": "## Probant \n\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. \n\n## Abit sua\n\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. " 185 | }, 186 | { 187 | "__component": "shared.quote", 188 | "title": "Thelonius Monk", 189 | "body": "You've got to dig it to dig it, you dig?" 190 | }, 191 | { 192 | "__component": "shared.media", 193 | "file": "coffee-art.jpg" 194 | }, 195 | { 196 | "__component": "shared.rich-text", 197 | "body": "## Spatiantia astra \n\nFoeda, medio silva *errandum*: onus formam munere. Mutata bibulis est auxiliare arces etiamnunc verbis virgineo Priamidas illa Thescelus, nam fit locis lucis auras. Exitus hospes gratulor ut pondere [speslimite](http://www.curas.io/figuram); quid habent, Avernales faciente de. Pervenit Ino sonabile supplex cognoscenti vires, Bacchumque errat miserarum venandi dignabere dedisti. Discrimina iuncosaque virgaque tot sine superest [fissus](http://quos.org/sitet.aspx). Non color esset potest non sumit, sed vix arserat. Nisi immo silva tantum pectusque quos pennis quisquam artus!" 198 | }, 199 | { 200 | "__component": "shared.slider", 201 | "files": ["coffee-art.jpg", "coffee-beans.jpg"] 202 | } 203 | ] 204 | }, 205 | { 206 | "title": "What's inside a Black Hole", 207 | "slug": "what-s-inside-a-black-hole", 208 | "category": { 209 | "id": 1 210 | }, 211 | "author": { 212 | "id": 2 213 | }, 214 | "description": "Maybe the answer is in this article, or not...", 215 | "cover": null, 216 | "blocks": [ 217 | { 218 | "__component": "shared.rich-text", 219 | "body": "## Probant \n\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. \n\n## Abit sua\n\nse Lorem markdownum negat. Argo *saxa* videnda cornuaque hunc qui tanta spes teneas! Obliquis est dicenti est salutat ille tamen iuvenum nostrae dolore. - Colores nocituraque comitata eripiunt - Addit quodcunque solum cui et dextram illis - Nulli meus nec extemplo ille ferebat pressit Se blandita fulvae vox gravem Pittheus cesserunt sanguine herbis tu comitum tenuit. Sui in ruunt; Doridaque maculosae fuissem! Et loqui. " 220 | }, 221 | { 222 | "__component": "shared.quote", 223 | "title": "Thelonius Monk", 224 | "body": "You've got to dig it to dig it, you dig?" 225 | }, 226 | { 227 | "__component": "shared.media", 228 | "file": "coffee-art.jpg" 229 | }, 230 | { 231 | "__component": "shared.rich-text", 232 | "body": "## Spatiantia astra \n\nFoeda, medio silva *errandum*: onus formam munere. Mutata bibulis est auxiliare arces etiamnunc verbis virgineo Priamidas illa Thescelus, nam fit locis lucis auras. Exitus hospes gratulor ut pondere [speslimite](http://www.curas.io/figuram); quid habent, Avernales faciente de. Pervenit Ino sonabile supplex cognoscenti vires, Bacchumque errat miserarum venandi dignabere dedisti. Discrimina iuncosaque virgaque tot sine superest [fissus](http://quos.org/sitet.aspx). Non color esset potest non sumit, sed vix arserat. Nisi immo silva tantum pectusque quos pennis quisquam artus!" 233 | }, 234 | { 235 | "__component": "shared.slider", 236 | "files": ["coffee-art.jpg", "coffee-beans.jpg"] 237 | } 238 | ] 239 | } 240 | ] 241 | } 242 | -------------------------------------------------------------------------------- /data/uploads/a-bug-is-becoming-a-meme-on-the-internet.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/data/uploads/a-bug-is-becoming-a-meme-on-the-internet.jpg -------------------------------------------------------------------------------- /data/uploads/beautiful-picture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/data/uploads/beautiful-picture.jpg -------------------------------------------------------------------------------- /data/uploads/coffee-art.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/data/uploads/coffee-art.jpg -------------------------------------------------------------------------------- /data/uploads/coffee-beans.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/data/uploads/coffee-beans.jpg -------------------------------------------------------------------------------- /data/uploads/coffee-shadow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/data/uploads/coffee-shadow.jpg -------------------------------------------------------------------------------- /data/uploads/daviddoe@strapi.io.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/data/uploads/daviddoe@strapi.io.jpg -------------------------------------------------------------------------------- /data/uploads/default-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/data/uploads/default-image.png -------------------------------------------------------------------------------- /data/uploads/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/data/uploads/favicon.png -------------------------------------------------------------------------------- /data/uploads/sarahbaker@strapi.io.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/data/uploads/sarahbaker@strapi.io.jpg -------------------------------------------------------------------------------- /data/uploads/the-internet-s-own-boy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/data/uploads/the-internet-s-own-boy.jpg -------------------------------------------------------------------------------- /data/uploads/this-shrimp-is-awesome.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/data/uploads/this-shrimp-is-awesome.jpg -------------------------------------------------------------------------------- /data/uploads/we-love-pizza.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/data/uploads/we-love-pizza.jpg -------------------------------------------------------------------------------- /data/uploads/what-s-inside-a-black-hole.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/data/uploads/what-s-inside-a-black-hole.jpg -------------------------------------------------------------------------------- /database/migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/database/migrations/.gitkeep -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/favicon.png -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "nodenext", 4 | "target": "ES2021", 5 | "checkJs": true, 6 | "allowJs": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wordpress-to-strapi-migration", 3 | "version": "0.1.0", 4 | "private": true, 5 | "description": "A Strapi application", 6 | "scripts": { 7 | "build": "strapi build", 8 | "deploy": "strapi deploy", 9 | "develop": "strapi develop", 10 | "seed:example": "node ./scripts/seed.js", 11 | "start": "strapi start", 12 | "strapi": "strapi" 13 | }, 14 | "dependencies": { 15 | "@strapi/plugin-cloud": "5.9.0", 16 | "@strapi/plugin-users-permissions": "5.9.0", 17 | "@strapi/strapi": "5.9.0", 18 | "better-sqlite3": "11.3.0", 19 | "dotenv": "^16.4.7", 20 | "fs-extra": "^10.0.0", 21 | "mime-types": "^2.1.27", 22 | "react": "^18.0.0", 23 | "react-dom": "^18.0.0", 24 | "react-router-dom": "^6.0.0", 25 | "styled-components": "^6.0.0" 26 | }, 27 | "engines": { 28 | "node": ">=18.0.0 <=22.x.x", 29 | "npm": ">=6.0.0" 30 | }, 31 | "strapi": { 32 | "uuid": "128e27ee-4f24-4129-a0ac-983ccb6ce827" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # To prevent search engines from seeing the site altogether, uncomment the next two lines: 2 | # User-Agent: * 3 | # Disallow: / 4 | -------------------------------------------------------------------------------- /public/uploads/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/public/uploads/.gitkeep -------------------------------------------------------------------------------- /scripts/seed.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs-extra'); 4 | const path = require('path'); 5 | const mime = require('mime-types'); 6 | const { categories, authors, articles, global, about } = require('../data/data.json'); 7 | 8 | async function seedExampleApp() { 9 | const shouldImportSeedData = await isFirstRun(); 10 | 11 | if (shouldImportSeedData) { 12 | try { 13 | console.log('Setting up the template...'); 14 | await importSeedData(); 15 | console.log('Ready to go'); 16 | } catch (error) { 17 | console.log('Could not import seed data'); 18 | console.error(error); 19 | } 20 | } else { 21 | console.log( 22 | 'Seed data has already been imported. We cannot reimport unless you clear your database first.' 23 | ); 24 | } 25 | } 26 | 27 | async function isFirstRun() { 28 | const pluginStore = strapi.store({ 29 | environment: strapi.config.environment, 30 | type: 'type', 31 | name: 'setup', 32 | }); 33 | const initHasRun = await pluginStore.get({ key: 'initHasRun' }); 34 | await pluginStore.set({ key: 'initHasRun', value: true }); 35 | return !initHasRun; 36 | } 37 | 38 | async function setPublicPermissions(newPermissions) { 39 | // Find the ID of the public role 40 | const publicRole = await strapi.query('plugin::users-permissions.role').findOne({ 41 | where: { 42 | type: 'public', 43 | }, 44 | }); 45 | 46 | // Create the new permissions and link them to the public role 47 | const allPermissionsToCreate = []; 48 | Object.keys(newPermissions).map((controller) => { 49 | const actions = newPermissions[controller]; 50 | const permissionsToCreate = actions.map((action) => { 51 | return strapi.query('plugin::users-permissions.permission').create({ 52 | data: { 53 | action: `api::${controller}.${controller}.${action}`, 54 | role: publicRole.id, 55 | }, 56 | }); 57 | }); 58 | allPermissionsToCreate.push(...permissionsToCreate); 59 | }); 60 | await Promise.all(allPermissionsToCreate); 61 | } 62 | 63 | function getFileSizeInBytes(filePath) { 64 | const stats = fs.statSync(filePath); 65 | const fileSizeInBytes = stats['size']; 66 | return fileSizeInBytes; 67 | } 68 | 69 | function getFileData(fileName) { 70 | const filePath = path.join('data', 'uploads', fileName); 71 | // Parse the file metadata 72 | const size = getFileSizeInBytes(filePath); 73 | const ext = fileName.split('.').pop(); 74 | const mimeType = mime.lookup(ext || '') || ''; 75 | 76 | return { 77 | filepath: filePath, 78 | originalFileName: fileName, 79 | size, 80 | mimetype: mimeType, 81 | }; 82 | } 83 | 84 | async function uploadFile(file, name) { 85 | return strapi 86 | .plugin('upload') 87 | .service('upload') 88 | .upload({ 89 | files: file, 90 | data: { 91 | fileInfo: { 92 | alternativeText: `An image uploaded to Strapi called ${name}`, 93 | caption: name, 94 | name, 95 | }, 96 | }, 97 | }); 98 | } 99 | 100 | // Create an entry and attach files if there are any 101 | async function createEntry({ model, entry }) { 102 | try { 103 | // Actually create the entry in Strapi 104 | await strapi.documents(`api::${model}.${model}`).create({ 105 | data: entry, 106 | }); 107 | } catch (error) { 108 | console.error({ model, entry, error }); 109 | } 110 | } 111 | 112 | async function checkFileExistsBeforeUpload(files) { 113 | const existingFiles = []; 114 | const uploadedFiles = []; 115 | const filesCopy = [...files]; 116 | 117 | for (const fileName of filesCopy) { 118 | // Check if the file already exists in Strapi 119 | const fileWhereName = await strapi.query('plugin::upload.file').findOne({ 120 | where: { 121 | name: fileName.replace(/\..*$/, ''), 122 | }, 123 | }); 124 | 125 | if (fileWhereName) { 126 | // File exists, don't upload it 127 | existingFiles.push(fileWhereName); 128 | } else { 129 | // File doesn't exist, upload it 130 | const fileData = getFileData(fileName); 131 | const fileNameNoExtension = fileName.split('.').shift(); 132 | const [file] = await uploadFile(fileData, fileNameNoExtension); 133 | uploadedFiles.push(file); 134 | } 135 | } 136 | const allFiles = [...existingFiles, ...uploadedFiles]; 137 | // If only one file then return only that file 138 | return allFiles.length === 1 ? allFiles[0] : allFiles; 139 | } 140 | 141 | async function updateBlocks(blocks) { 142 | const updatedBlocks = []; 143 | for (const block of blocks) { 144 | if (block.__component === 'shared.media') { 145 | const uploadedFiles = await checkFileExistsBeforeUpload([block.file]); 146 | // Copy the block to not mutate directly 147 | const blockCopy = { ...block }; 148 | // Replace the file name on the block with the actual file 149 | blockCopy.file = uploadedFiles; 150 | updatedBlocks.push(blockCopy); 151 | } else if (block.__component === 'shared.slider') { 152 | // Get files already uploaded to Strapi or upload new files 153 | const existingAndUploadedFiles = await checkFileExistsBeforeUpload(block.files); 154 | // Copy the block to not mutate directly 155 | const blockCopy = { ...block }; 156 | // Replace the file names on the block with the actual files 157 | blockCopy.files = existingAndUploadedFiles; 158 | // Push the updated block 159 | updatedBlocks.push(blockCopy); 160 | } else { 161 | // Just push the block as is 162 | updatedBlocks.push(block); 163 | } 164 | } 165 | 166 | return updatedBlocks; 167 | } 168 | 169 | async function importArticles() { 170 | for (const article of articles) { 171 | const cover = await checkFileExistsBeforeUpload([`${article.slug}.jpg`]); 172 | const updatedBlocks = await updateBlocks(article.blocks); 173 | 174 | await createEntry({ 175 | model: 'article', 176 | entry: { 177 | ...article, 178 | cover, 179 | blocks: updatedBlocks, 180 | // Make sure it's not a draft 181 | publishedAt: Date.now(), 182 | }, 183 | }); 184 | } 185 | } 186 | 187 | async function importGlobal() { 188 | const favicon = await checkFileExistsBeforeUpload(['favicon.png']); 189 | const shareImage = await checkFileExistsBeforeUpload(['default-image.png']); 190 | return createEntry({ 191 | model: 'global', 192 | entry: { 193 | ...global, 194 | favicon, 195 | // Make sure it's not a draft 196 | publishedAt: Date.now(), 197 | defaultSeo: { 198 | ...global.defaultSeo, 199 | shareImage, 200 | }, 201 | }, 202 | }); 203 | } 204 | 205 | async function importAbout() { 206 | const updatedBlocks = await updateBlocks(about.blocks); 207 | 208 | await createEntry({ 209 | model: 'about', 210 | entry: { 211 | ...about, 212 | blocks: updatedBlocks, 213 | // Make sure it's not a draft 214 | publishedAt: Date.now(), 215 | }, 216 | }); 217 | } 218 | 219 | async function importCategories() { 220 | for (const category of categories) { 221 | await createEntry({ model: 'category', entry: category }); 222 | } 223 | } 224 | 225 | async function importAuthors() { 226 | for (const author of authors) { 227 | const avatar = await checkFileExistsBeforeUpload([author.avatar]); 228 | 229 | await createEntry({ 230 | model: 'author', 231 | entry: { 232 | ...author, 233 | avatar, 234 | }, 235 | }); 236 | } 237 | } 238 | 239 | async function importSeedData() { 240 | // Allow read of application content types 241 | await setPublicPermissions({ 242 | article: ['find', 'findOne'], 243 | category: ['find', 'findOne'], 244 | author: ['find', 'findOne'], 245 | global: ['find', 'findOne'], 246 | about: ['find', 'findOne'], 247 | }); 248 | 249 | // Create all entries 250 | await importCategories(); 251 | await importAuthors(); 252 | await importArticles(); 253 | await importGlobal(); 254 | await importAbout(); 255 | } 256 | 257 | async function main() { 258 | const { createStrapi, compileStrapi } = require('@strapi/strapi'); 259 | 260 | const appContext = await compileStrapi(); 261 | const app = await createStrapi(appContext).load(); 262 | 263 | app.log.level = 'error'; 264 | 265 | await seedExampleApp(); 266 | await app.destroy(); 267 | 268 | process.exit(0); 269 | } 270 | 271 | main().catch((error) => { 272 | console.error(error); 273 | process.exit(1); 274 | }); 275 | -------------------------------------------------------------------------------- /src/admin/app.example.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | locales: [ 3 | // 'ar', 4 | // 'fr', 5 | // 'cs', 6 | // 'de', 7 | // 'dk', 8 | // 'es', 9 | // 'he', 10 | // 'id', 11 | // 'it', 12 | // 'ja', 13 | // 'ko', 14 | // 'ms', 15 | // 'nl', 16 | // 'no', 17 | // 'pl', 18 | // 'pt-BR', 19 | // 'pt', 20 | // 'ru', 21 | // 'sk', 22 | // 'sv', 23 | // 'th', 24 | // 'tr', 25 | // 'uk', 26 | // 'vi', 27 | // 'zh-Hans', 28 | // 'zh', 29 | ], 30 | }; 31 | 32 | const bootstrap = (app) => { 33 | console.log(app); 34 | }; 35 | 36 | export default { 37 | config, 38 | bootstrap, 39 | }; 40 | -------------------------------------------------------------------------------- /src/admin/vite.config.example.js: -------------------------------------------------------------------------------- 1 | const { mergeConfig } = require('vite'); 2 | 3 | module.exports = (config) => { 4 | // Important: always return the modified config 5 | return mergeConfig(config, { 6 | resolve: { 7 | alias: { 8 | '@': '/src', 9 | }, 10 | }, 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /src/api/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/src/api/.gitkeep -------------------------------------------------------------------------------- /src/api/about/content-types/about/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "singleType", 3 | "collectionName": "abouts", 4 | "info": { 5 | "singularName": "about", 6 | "pluralName": "abouts", 7 | "displayName": "About", 8 | "description": "Write about yourself and the content you create" 9 | }, 10 | "options": { 11 | "draftAndPublish": false 12 | }, 13 | "pluginOptions": {}, 14 | "attributes": { 15 | "title": { 16 | "type": "string" 17 | }, 18 | "blocks": { 19 | "type": "dynamiczone", 20 | "components": ["shared.media", "shared.quote", "shared.rich-text", "shared.slider"] 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/api/about/controllers/about.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * about controller 5 | */ 6 | 7 | const { createCoreController } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreController('api::about.about'); 10 | -------------------------------------------------------------------------------- /src/api/about/routes/about.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * about router. 5 | */ 6 | 7 | const { createCoreRouter } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreRouter('api::about.about'); 10 | -------------------------------------------------------------------------------- /src/api/about/services/about.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * about service. 5 | */ 6 | 7 | const { createCoreService } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreService('api::about.about'); 10 | -------------------------------------------------------------------------------- /src/api/article/content-types/article/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "collectionType", 3 | "collectionName": "articles", 4 | "info": { 5 | "singularName": "article", 6 | "pluralName": "articles", 7 | "displayName": "Article", 8 | "description": "Create your blog content" 9 | }, 10 | "options": { 11 | "draftAndPublish": true 12 | }, 13 | "pluginOptions": {}, 14 | "attributes": { 15 | "title": { 16 | "type": "string" 17 | }, 18 | "slug": { 19 | "type": "uid", 20 | "targetField": "title" 21 | }, 22 | "featured_image": { 23 | "type": "media", 24 | "multiple": false, 25 | "required": false, 26 | "allowedTypes": [ 27 | "images", 28 | "files", 29 | "videos" 30 | ] 31 | }, 32 | "author": { 33 | "type": "relation", 34 | "relation": "manyToOne", 35 | "target": "api::author.author", 36 | "inversedBy": "articles" 37 | }, 38 | "category": { 39 | "type": "relation", 40 | "relation": "manyToOne", 41 | "target": "api::category.category", 42 | "inversedBy": "articles" 43 | }, 44 | "blocks": { 45 | "type": "dynamiczone", 46 | "components": [ 47 | "shared.media", 48 | "shared.quote", 49 | "shared.rich-text", 50 | "shared.slider" 51 | ] 52 | }, 53 | "content": { 54 | "type": "blocks" 55 | }, 56 | "date": { 57 | "type": "date" 58 | }, 59 | "excerpt": { 60 | "type": "text" 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/api/article/controllers/article.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * article controller 5 | */ 6 | 7 | const { createCoreController } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreController('api::article.article'); 10 | -------------------------------------------------------------------------------- /src/api/article/routes/article.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * article router. 5 | */ 6 | 7 | const { createCoreRouter } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreRouter('api::article.article'); 10 | -------------------------------------------------------------------------------- /src/api/article/services/article.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * article service. 5 | */ 6 | 7 | const { createCoreService } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreService('api::article.article'); 10 | -------------------------------------------------------------------------------- /src/api/author/content-types/author/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "collectionType", 3 | "collectionName": "authors", 4 | "info": { 5 | "singularName": "author", 6 | "pluralName": "authors", 7 | "displayName": "Author", 8 | "description": "Create authors for your content" 9 | }, 10 | "options": { 11 | "draftAndPublish": false 12 | }, 13 | "pluginOptions": {}, 14 | "attributes": { 15 | "name": { 16 | "type": "string" 17 | }, 18 | "avatar": { 19 | "type": "media", 20 | "multiple": false, 21 | "required": false, 22 | "allowedTypes": [ 23 | "images", 24 | "files", 25 | "videos" 26 | ] 27 | }, 28 | "email": { 29 | "type": "string" 30 | }, 31 | "articles": { 32 | "type": "relation", 33 | "relation": "oneToMany", 34 | "target": "api::article.article", 35 | "mappedBy": "author" 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/api/author/controllers/author.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * author controller 5 | */ 6 | 7 | const { createCoreController } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreController('api::author.author'); 10 | -------------------------------------------------------------------------------- /src/api/author/routes/author.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * author router. 5 | */ 6 | 7 | const { createCoreRouter } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreRouter('api::author.author'); 10 | -------------------------------------------------------------------------------- /src/api/author/services/author.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * author service. 5 | */ 6 | 7 | const { createCoreService } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreService('api::author.author'); 10 | -------------------------------------------------------------------------------- /src/api/category/content-types/category/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "collectionType", 3 | "collectionName": "categories", 4 | "info": { 5 | "singularName": "category", 6 | "pluralName": "categories", 7 | "displayName": "Category", 8 | "description": "Organize your content into categories" 9 | }, 10 | "options": { 11 | "draftAndPublish": false 12 | }, 13 | "pluginOptions": {}, 14 | "attributes": { 15 | "name": { 16 | "type": "string" 17 | }, 18 | "slug": { 19 | "type": "uid" 20 | }, 21 | "articles": { 22 | "type": "relation", 23 | "relation": "oneToMany", 24 | "target": "api::article.article", 25 | "mappedBy": "category" 26 | }, 27 | "description": { 28 | "type": "text" 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/api/category/controllers/category.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * category controller 5 | */ 6 | 7 | const { createCoreController } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreController('api::category.category'); 10 | -------------------------------------------------------------------------------- /src/api/category/routes/category.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * category router. 5 | */ 6 | 7 | const { createCoreRouter } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreRouter('api::category.category'); 10 | -------------------------------------------------------------------------------- /src/api/category/services/category.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * category service. 5 | */ 6 | 7 | const { createCoreService } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreService('api::category.category'); 10 | -------------------------------------------------------------------------------- /src/api/global/content-types/global/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "singleType", 3 | "collectionName": "globals", 4 | "info": { 5 | "singularName": "global", 6 | "pluralName": "globals", 7 | "displayName": "Global", 8 | "description": "Define global settings" 9 | }, 10 | "options": { 11 | "draftAndPublish": false 12 | }, 13 | "pluginOptions": {}, 14 | "attributes": { 15 | "siteName": { 16 | "type": "string", 17 | "required": true 18 | }, 19 | "favicon": { 20 | "type": "media", 21 | "multiple": false, 22 | "required": false, 23 | "allowedTypes": ["images", "files", "videos"] 24 | }, 25 | "siteDescription": { 26 | "type": "text", 27 | "required": true 28 | }, 29 | "defaultSeo": { 30 | "type": "component", 31 | "repeatable": false, 32 | "component": "shared.seo" 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/api/global/controllers/global.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * global controller 5 | */ 6 | 7 | const { createCoreController } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreController('api::global.global'); 10 | -------------------------------------------------------------------------------- /src/api/global/routes/global.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * global router. 5 | */ 6 | 7 | const { createCoreRouter } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreRouter('api::global.global'); 10 | -------------------------------------------------------------------------------- /src/api/global/services/global.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * global service. 5 | */ 6 | 7 | const { createCoreService } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreService('api::global.global'); 10 | -------------------------------------------------------------------------------- /src/api/post/content-types/post/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "collectionType", 3 | "collectionName": "posts", 4 | "info": { 5 | "singularName": "post", 6 | "pluralName": "posts", 7 | "displayName": "Post", 8 | "description": "" 9 | }, 10 | "options": { 11 | "draftAndPublish": true 12 | }, 13 | "pluginOptions": {}, 14 | "attributes": { 15 | "title": { 16 | "type": "string" 17 | }, 18 | "excerpt": { 19 | "type": "string" 20 | }, 21 | "author": { 22 | "type": "string" 23 | }, 24 | "date": { 25 | "type": "date" 26 | }, 27 | "featured_image": { 28 | "type": "media", 29 | "multiple": false, 30 | "required": false, 31 | "allowedTypes": [ 32 | "images" 33 | ] 34 | }, 35 | "post_number": { 36 | "type": "integer" 37 | }, 38 | "slug": { 39 | "type": "uid", 40 | "targetField": "title" 41 | }, 42 | "content": { 43 | "type": "richtext" 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/api/post/controllers/post.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * post controller 5 | */ 6 | 7 | const { createCoreController } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreController('api::post.post'); 10 | -------------------------------------------------------------------------------- /src/api/post/routes/post.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * post router 5 | */ 6 | 7 | const { createCoreRouter } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreRouter('api::post.post'); 10 | -------------------------------------------------------------------------------- /src/api/post/services/post.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * post service 5 | */ 6 | 7 | const { createCoreService } = require('@strapi/strapi').factories; 8 | 9 | module.exports = createCoreService('api::post.post'); 10 | -------------------------------------------------------------------------------- /src/components/shared/media.json: -------------------------------------------------------------------------------- 1 | { 2 | "collectionName": "components_shared_media", 3 | "info": { 4 | "displayName": "Media", 5 | "icon": "file-video" 6 | }, 7 | "options": {}, 8 | "attributes": { 9 | "file": { 10 | "allowedTypes": ["images", "files", "videos"], 11 | "type": "media", 12 | "multiple": false 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/components/shared/quote.json: -------------------------------------------------------------------------------- 1 | { 2 | "collectionName": "components_shared_quotes", 3 | "info": { 4 | "displayName": "Quote", 5 | "icon": "indent" 6 | }, 7 | "options": {}, 8 | "attributes": { 9 | "title": { 10 | "type": "string" 11 | }, 12 | "body": { 13 | "type": "text" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/components/shared/rich-text.json: -------------------------------------------------------------------------------- 1 | { 2 | "collectionName": "components_shared_rich_texts", 3 | "info": { 4 | "displayName": "Rich text", 5 | "icon": "align-justify", 6 | "description": "" 7 | }, 8 | "options": {}, 9 | "attributes": { 10 | "body": { 11 | "type": "richtext" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/shared/seo.json: -------------------------------------------------------------------------------- 1 | { 2 | "collectionName": "components_shared_seos", 3 | "info": { 4 | "name": "Seo", 5 | "icon": "allergies", 6 | "displayName": "Seo", 7 | "description": "" 8 | }, 9 | "options": {}, 10 | "attributes": { 11 | "metaTitle": { 12 | "type": "string", 13 | "required": true 14 | }, 15 | "metaDescription": { 16 | "type": "text", 17 | "required": true 18 | }, 19 | "shareImage": { 20 | "type": "media", 21 | "multiple": false, 22 | "required": false, 23 | "allowedTypes": ["images"] 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/components/shared/slider.json: -------------------------------------------------------------------------------- 1 | { 2 | "collectionName": "components_shared_sliders", 3 | "info": { 4 | "displayName": "Slider", 5 | "icon": "address-book", 6 | "description": "" 7 | }, 8 | "options": {}, 9 | "attributes": { 10 | "files": { 11 | "type": "media", 12 | "multiple": true, 13 | "required": false, 14 | "allowedTypes": ["images"] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/extensions/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rwit-io/wordpress-to-strapi/3b467ca9a0a0b8c5f8b77409e7fdd28685e64d32/src/extensions/.gitkeep -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | /** 5 | * An asynchronous register function that runs before 6 | * your application is initialized. 7 | * 8 | * This gives you an opportunity to extend code. 9 | */ 10 | register(/*{ strapi }*/) {}, 11 | 12 | /** 13 | * An asynchronous bootstrap function that runs before 14 | * your application gets started. 15 | * 16 | * This gives you an opportunity to set up your data model, 17 | * run jobs, or perform some special logic. 18 | */ 19 | bootstrap(/*{ strapi }*/) {}, 20 | }; 21 | -------------------------------------------------------------------------------- /types/generated/components.d.ts: -------------------------------------------------------------------------------- 1 | import type { Schema, Struct } from '@strapi/strapi'; 2 | 3 | export interface SharedMedia extends Struct.ComponentSchema { 4 | collectionName: 'components_shared_media'; 5 | info: { 6 | displayName: 'Media'; 7 | icon: 'file-video'; 8 | }; 9 | attributes: { 10 | file: Schema.Attribute.Media<'images' | 'files' | 'videos'>; 11 | }; 12 | } 13 | 14 | export interface SharedQuote extends Struct.ComponentSchema { 15 | collectionName: 'components_shared_quotes'; 16 | info: { 17 | displayName: 'Quote'; 18 | icon: 'indent'; 19 | }; 20 | attributes: { 21 | body: Schema.Attribute.Text; 22 | title: Schema.Attribute.String; 23 | }; 24 | } 25 | 26 | export interface SharedRichText extends Struct.ComponentSchema { 27 | collectionName: 'components_shared_rich_texts'; 28 | info: { 29 | description: ''; 30 | displayName: 'Rich text'; 31 | icon: 'align-justify'; 32 | }; 33 | attributes: { 34 | body: Schema.Attribute.RichText; 35 | }; 36 | } 37 | 38 | export interface SharedSeo extends Struct.ComponentSchema { 39 | collectionName: 'components_shared_seos'; 40 | info: { 41 | description: ''; 42 | displayName: 'Seo'; 43 | icon: 'allergies'; 44 | name: 'Seo'; 45 | }; 46 | attributes: { 47 | metaDescription: Schema.Attribute.Text & Schema.Attribute.Required; 48 | metaTitle: Schema.Attribute.String & Schema.Attribute.Required; 49 | shareImage: Schema.Attribute.Media<'images'>; 50 | }; 51 | } 52 | 53 | export interface SharedSlider extends Struct.ComponentSchema { 54 | collectionName: 'components_shared_sliders'; 55 | info: { 56 | description: ''; 57 | displayName: 'Slider'; 58 | icon: 'address-book'; 59 | }; 60 | attributes: { 61 | files: Schema.Attribute.Media<'images', true>; 62 | }; 63 | } 64 | 65 | declare module '@strapi/strapi' { 66 | export module Public { 67 | export interface ComponentSchemas { 68 | 'shared.media': SharedMedia; 69 | 'shared.quote': SharedQuote; 70 | 'shared.rich-text': SharedRichText; 71 | 'shared.seo': SharedSeo; 72 | 'shared.slider': SharedSlider; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /types/generated/contentTypes.d.ts: -------------------------------------------------------------------------------- 1 | import type { Schema, Struct } from '@strapi/strapi'; 2 | 3 | export interface AdminApiToken extends Struct.CollectionTypeSchema { 4 | collectionName: 'strapi_api_tokens'; 5 | info: { 6 | description: ''; 7 | displayName: 'Api Token'; 8 | name: 'Api Token'; 9 | pluralName: 'api-tokens'; 10 | singularName: 'api-token'; 11 | }; 12 | options: { 13 | draftAndPublish: false; 14 | }; 15 | pluginOptions: { 16 | 'content-manager': { 17 | visible: false; 18 | }; 19 | 'content-type-builder': { 20 | visible: false; 21 | }; 22 | }; 23 | attributes: { 24 | accessKey: Schema.Attribute.String & 25 | Schema.Attribute.Required & 26 | Schema.Attribute.SetMinMaxLength<{ 27 | minLength: 1; 28 | }>; 29 | createdAt: Schema.Attribute.DateTime; 30 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 31 | Schema.Attribute.Private; 32 | description: Schema.Attribute.String & 33 | Schema.Attribute.SetMinMaxLength<{ 34 | minLength: 1; 35 | }> & 36 | Schema.Attribute.DefaultTo<''>; 37 | expiresAt: Schema.Attribute.DateTime; 38 | lastUsedAt: Schema.Attribute.DateTime; 39 | lifespan: Schema.Attribute.BigInteger; 40 | locale: Schema.Attribute.String & Schema.Attribute.Private; 41 | localizations: Schema.Attribute.Relation<'oneToMany', 'admin::api-token'> & 42 | Schema.Attribute.Private; 43 | name: Schema.Attribute.String & 44 | Schema.Attribute.Required & 45 | Schema.Attribute.Unique & 46 | Schema.Attribute.SetMinMaxLength<{ 47 | minLength: 1; 48 | }>; 49 | permissions: Schema.Attribute.Relation< 50 | 'oneToMany', 51 | 'admin::api-token-permission' 52 | >; 53 | publishedAt: Schema.Attribute.DateTime; 54 | type: Schema.Attribute.Enumeration<['read-only', 'full-access', 'custom']> & 55 | Schema.Attribute.Required & 56 | Schema.Attribute.DefaultTo<'read-only'>; 57 | updatedAt: Schema.Attribute.DateTime; 58 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 59 | Schema.Attribute.Private; 60 | }; 61 | } 62 | 63 | export interface AdminApiTokenPermission extends Struct.CollectionTypeSchema { 64 | collectionName: 'strapi_api_token_permissions'; 65 | info: { 66 | description: ''; 67 | displayName: 'API Token Permission'; 68 | name: 'API Token Permission'; 69 | pluralName: 'api-token-permissions'; 70 | singularName: 'api-token-permission'; 71 | }; 72 | options: { 73 | draftAndPublish: false; 74 | }; 75 | pluginOptions: { 76 | 'content-manager': { 77 | visible: false; 78 | }; 79 | 'content-type-builder': { 80 | visible: false; 81 | }; 82 | }; 83 | attributes: { 84 | action: Schema.Attribute.String & 85 | Schema.Attribute.Required & 86 | Schema.Attribute.SetMinMaxLength<{ 87 | minLength: 1; 88 | }>; 89 | createdAt: Schema.Attribute.DateTime; 90 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 91 | Schema.Attribute.Private; 92 | locale: Schema.Attribute.String & Schema.Attribute.Private; 93 | localizations: Schema.Attribute.Relation< 94 | 'oneToMany', 95 | 'admin::api-token-permission' 96 | > & 97 | Schema.Attribute.Private; 98 | publishedAt: Schema.Attribute.DateTime; 99 | token: Schema.Attribute.Relation<'manyToOne', 'admin::api-token'>; 100 | updatedAt: Schema.Attribute.DateTime; 101 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 102 | Schema.Attribute.Private; 103 | }; 104 | } 105 | 106 | export interface AdminPermission extends Struct.CollectionTypeSchema { 107 | collectionName: 'admin_permissions'; 108 | info: { 109 | description: ''; 110 | displayName: 'Permission'; 111 | name: 'Permission'; 112 | pluralName: 'permissions'; 113 | singularName: 'permission'; 114 | }; 115 | options: { 116 | draftAndPublish: false; 117 | }; 118 | pluginOptions: { 119 | 'content-manager': { 120 | visible: false; 121 | }; 122 | 'content-type-builder': { 123 | visible: false; 124 | }; 125 | }; 126 | attributes: { 127 | action: Schema.Attribute.String & 128 | Schema.Attribute.Required & 129 | Schema.Attribute.SetMinMaxLength<{ 130 | minLength: 1; 131 | }>; 132 | actionParameters: Schema.Attribute.JSON & Schema.Attribute.DefaultTo<{}>; 133 | conditions: Schema.Attribute.JSON & Schema.Attribute.DefaultTo<[]>; 134 | createdAt: Schema.Attribute.DateTime; 135 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 136 | Schema.Attribute.Private; 137 | locale: Schema.Attribute.String & Schema.Attribute.Private; 138 | localizations: Schema.Attribute.Relation<'oneToMany', 'admin::permission'> & 139 | Schema.Attribute.Private; 140 | properties: Schema.Attribute.JSON & Schema.Attribute.DefaultTo<{}>; 141 | publishedAt: Schema.Attribute.DateTime; 142 | role: Schema.Attribute.Relation<'manyToOne', 'admin::role'>; 143 | subject: Schema.Attribute.String & 144 | Schema.Attribute.SetMinMaxLength<{ 145 | minLength: 1; 146 | }>; 147 | updatedAt: Schema.Attribute.DateTime; 148 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 149 | Schema.Attribute.Private; 150 | }; 151 | } 152 | 153 | export interface AdminRole extends Struct.CollectionTypeSchema { 154 | collectionName: 'admin_roles'; 155 | info: { 156 | description: ''; 157 | displayName: 'Role'; 158 | name: 'Role'; 159 | pluralName: 'roles'; 160 | singularName: 'role'; 161 | }; 162 | options: { 163 | draftAndPublish: false; 164 | }; 165 | pluginOptions: { 166 | 'content-manager': { 167 | visible: false; 168 | }; 169 | 'content-type-builder': { 170 | visible: false; 171 | }; 172 | }; 173 | attributes: { 174 | code: Schema.Attribute.String & 175 | Schema.Attribute.Required & 176 | Schema.Attribute.Unique & 177 | Schema.Attribute.SetMinMaxLength<{ 178 | minLength: 1; 179 | }>; 180 | createdAt: Schema.Attribute.DateTime; 181 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 182 | Schema.Attribute.Private; 183 | description: Schema.Attribute.String; 184 | locale: Schema.Attribute.String & Schema.Attribute.Private; 185 | localizations: Schema.Attribute.Relation<'oneToMany', 'admin::role'> & 186 | Schema.Attribute.Private; 187 | name: Schema.Attribute.String & 188 | Schema.Attribute.Required & 189 | Schema.Attribute.Unique & 190 | Schema.Attribute.SetMinMaxLength<{ 191 | minLength: 1; 192 | }>; 193 | permissions: Schema.Attribute.Relation<'oneToMany', 'admin::permission'>; 194 | publishedAt: Schema.Attribute.DateTime; 195 | updatedAt: Schema.Attribute.DateTime; 196 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 197 | Schema.Attribute.Private; 198 | users: Schema.Attribute.Relation<'manyToMany', 'admin::user'>; 199 | }; 200 | } 201 | 202 | export interface AdminTransferToken extends Struct.CollectionTypeSchema { 203 | collectionName: 'strapi_transfer_tokens'; 204 | info: { 205 | description: ''; 206 | displayName: 'Transfer Token'; 207 | name: 'Transfer Token'; 208 | pluralName: 'transfer-tokens'; 209 | singularName: 'transfer-token'; 210 | }; 211 | options: { 212 | draftAndPublish: false; 213 | }; 214 | pluginOptions: { 215 | 'content-manager': { 216 | visible: false; 217 | }; 218 | 'content-type-builder': { 219 | visible: false; 220 | }; 221 | }; 222 | attributes: { 223 | accessKey: Schema.Attribute.String & 224 | Schema.Attribute.Required & 225 | Schema.Attribute.SetMinMaxLength<{ 226 | minLength: 1; 227 | }>; 228 | createdAt: Schema.Attribute.DateTime; 229 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 230 | Schema.Attribute.Private; 231 | description: Schema.Attribute.String & 232 | Schema.Attribute.SetMinMaxLength<{ 233 | minLength: 1; 234 | }> & 235 | Schema.Attribute.DefaultTo<''>; 236 | expiresAt: Schema.Attribute.DateTime; 237 | lastUsedAt: Schema.Attribute.DateTime; 238 | lifespan: Schema.Attribute.BigInteger; 239 | locale: Schema.Attribute.String & Schema.Attribute.Private; 240 | localizations: Schema.Attribute.Relation< 241 | 'oneToMany', 242 | 'admin::transfer-token' 243 | > & 244 | Schema.Attribute.Private; 245 | name: Schema.Attribute.String & 246 | Schema.Attribute.Required & 247 | Schema.Attribute.Unique & 248 | Schema.Attribute.SetMinMaxLength<{ 249 | minLength: 1; 250 | }>; 251 | permissions: Schema.Attribute.Relation< 252 | 'oneToMany', 253 | 'admin::transfer-token-permission' 254 | >; 255 | publishedAt: Schema.Attribute.DateTime; 256 | updatedAt: Schema.Attribute.DateTime; 257 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 258 | Schema.Attribute.Private; 259 | }; 260 | } 261 | 262 | export interface AdminTransferTokenPermission 263 | extends Struct.CollectionTypeSchema { 264 | collectionName: 'strapi_transfer_token_permissions'; 265 | info: { 266 | description: ''; 267 | displayName: 'Transfer Token Permission'; 268 | name: 'Transfer Token Permission'; 269 | pluralName: 'transfer-token-permissions'; 270 | singularName: 'transfer-token-permission'; 271 | }; 272 | options: { 273 | draftAndPublish: false; 274 | }; 275 | pluginOptions: { 276 | 'content-manager': { 277 | visible: false; 278 | }; 279 | 'content-type-builder': { 280 | visible: false; 281 | }; 282 | }; 283 | attributes: { 284 | action: Schema.Attribute.String & 285 | Schema.Attribute.Required & 286 | Schema.Attribute.SetMinMaxLength<{ 287 | minLength: 1; 288 | }>; 289 | createdAt: Schema.Attribute.DateTime; 290 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 291 | Schema.Attribute.Private; 292 | locale: Schema.Attribute.String & Schema.Attribute.Private; 293 | localizations: Schema.Attribute.Relation< 294 | 'oneToMany', 295 | 'admin::transfer-token-permission' 296 | > & 297 | Schema.Attribute.Private; 298 | publishedAt: Schema.Attribute.DateTime; 299 | token: Schema.Attribute.Relation<'manyToOne', 'admin::transfer-token'>; 300 | updatedAt: Schema.Attribute.DateTime; 301 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 302 | Schema.Attribute.Private; 303 | }; 304 | } 305 | 306 | export interface AdminUser extends Struct.CollectionTypeSchema { 307 | collectionName: 'admin_users'; 308 | info: { 309 | description: ''; 310 | displayName: 'User'; 311 | name: 'User'; 312 | pluralName: 'users'; 313 | singularName: 'user'; 314 | }; 315 | options: { 316 | draftAndPublish: false; 317 | }; 318 | pluginOptions: { 319 | 'content-manager': { 320 | visible: false; 321 | }; 322 | 'content-type-builder': { 323 | visible: false; 324 | }; 325 | }; 326 | attributes: { 327 | blocked: Schema.Attribute.Boolean & 328 | Schema.Attribute.Private & 329 | Schema.Attribute.DefaultTo; 330 | createdAt: Schema.Attribute.DateTime; 331 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 332 | Schema.Attribute.Private; 333 | email: Schema.Attribute.Email & 334 | Schema.Attribute.Required & 335 | Schema.Attribute.Private & 336 | Schema.Attribute.Unique & 337 | Schema.Attribute.SetMinMaxLength<{ 338 | minLength: 6; 339 | }>; 340 | firstname: Schema.Attribute.String & 341 | Schema.Attribute.SetMinMaxLength<{ 342 | minLength: 1; 343 | }>; 344 | isActive: Schema.Attribute.Boolean & 345 | Schema.Attribute.Private & 346 | Schema.Attribute.DefaultTo; 347 | lastname: Schema.Attribute.String & 348 | Schema.Attribute.SetMinMaxLength<{ 349 | minLength: 1; 350 | }>; 351 | locale: Schema.Attribute.String & Schema.Attribute.Private; 352 | localizations: Schema.Attribute.Relation<'oneToMany', 'admin::user'> & 353 | Schema.Attribute.Private; 354 | password: Schema.Attribute.Password & 355 | Schema.Attribute.Private & 356 | Schema.Attribute.SetMinMaxLength<{ 357 | minLength: 6; 358 | }>; 359 | preferedLanguage: Schema.Attribute.String; 360 | publishedAt: Schema.Attribute.DateTime; 361 | registrationToken: Schema.Attribute.String & Schema.Attribute.Private; 362 | resetPasswordToken: Schema.Attribute.String & Schema.Attribute.Private; 363 | roles: Schema.Attribute.Relation<'manyToMany', 'admin::role'> & 364 | Schema.Attribute.Private; 365 | updatedAt: Schema.Attribute.DateTime; 366 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 367 | Schema.Attribute.Private; 368 | username: Schema.Attribute.String; 369 | }; 370 | } 371 | 372 | export interface ApiAboutAbout extends Struct.SingleTypeSchema { 373 | collectionName: 'abouts'; 374 | info: { 375 | description: 'Write about yourself and the content you create'; 376 | displayName: 'About'; 377 | pluralName: 'abouts'; 378 | singularName: 'about'; 379 | }; 380 | options: { 381 | draftAndPublish: false; 382 | }; 383 | attributes: { 384 | blocks: Schema.Attribute.DynamicZone< 385 | ['shared.media', 'shared.quote', 'shared.rich-text', 'shared.slider'] 386 | >; 387 | createdAt: Schema.Attribute.DateTime; 388 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 389 | Schema.Attribute.Private; 390 | locale: Schema.Attribute.String & Schema.Attribute.Private; 391 | localizations: Schema.Attribute.Relation<'oneToMany', 'api::about.about'> & 392 | Schema.Attribute.Private; 393 | publishedAt: Schema.Attribute.DateTime; 394 | title: Schema.Attribute.String; 395 | updatedAt: Schema.Attribute.DateTime; 396 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 397 | Schema.Attribute.Private; 398 | }; 399 | } 400 | 401 | export interface ApiArticleArticle extends Struct.CollectionTypeSchema { 402 | collectionName: 'articles'; 403 | info: { 404 | description: 'Create your blog content'; 405 | displayName: 'Article'; 406 | pluralName: 'articles'; 407 | singularName: 'article'; 408 | }; 409 | options: { 410 | draftAndPublish: true; 411 | }; 412 | attributes: { 413 | author: Schema.Attribute.Relation<'manyToOne', 'api::author.author'>; 414 | blocks: Schema.Attribute.DynamicZone< 415 | ['shared.media', 'shared.quote', 'shared.rich-text', 'shared.slider'] 416 | >; 417 | category: Schema.Attribute.Relation<'manyToOne', 'api::category.category'>; 418 | content: Schema.Attribute.Blocks; 419 | createdAt: Schema.Attribute.DateTime; 420 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 421 | Schema.Attribute.Private; 422 | date: Schema.Attribute.Date; 423 | excerpt: Schema.Attribute.Text; 424 | featured_image: Schema.Attribute.Media<'images' | 'files' | 'videos'>; 425 | locale: Schema.Attribute.String & Schema.Attribute.Private; 426 | localizations: Schema.Attribute.Relation< 427 | 'oneToMany', 428 | 'api::article.article' 429 | > & 430 | Schema.Attribute.Private; 431 | publishedAt: Schema.Attribute.DateTime; 432 | slug: Schema.Attribute.UID<'title'>; 433 | title: Schema.Attribute.String; 434 | updatedAt: Schema.Attribute.DateTime; 435 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 436 | Schema.Attribute.Private; 437 | }; 438 | } 439 | 440 | export interface ApiAuthorAuthor extends Struct.CollectionTypeSchema { 441 | collectionName: 'authors'; 442 | info: { 443 | description: 'Create authors for your content'; 444 | displayName: 'Author'; 445 | pluralName: 'authors'; 446 | singularName: 'author'; 447 | }; 448 | options: { 449 | draftAndPublish: false; 450 | }; 451 | attributes: { 452 | articles: Schema.Attribute.Relation<'oneToMany', 'api::article.article'>; 453 | avatar: Schema.Attribute.Media<'images' | 'files' | 'videos'>; 454 | createdAt: Schema.Attribute.DateTime; 455 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 456 | Schema.Attribute.Private; 457 | email: Schema.Attribute.String; 458 | locale: Schema.Attribute.String & Schema.Attribute.Private; 459 | localizations: Schema.Attribute.Relation< 460 | 'oneToMany', 461 | 'api::author.author' 462 | > & 463 | Schema.Attribute.Private; 464 | name: Schema.Attribute.String; 465 | publishedAt: Schema.Attribute.DateTime; 466 | updatedAt: Schema.Attribute.DateTime; 467 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 468 | Schema.Attribute.Private; 469 | }; 470 | } 471 | 472 | export interface ApiCategoryCategory extends Struct.CollectionTypeSchema { 473 | collectionName: 'categories'; 474 | info: { 475 | description: 'Organize your content into categories'; 476 | displayName: 'Category'; 477 | pluralName: 'categories'; 478 | singularName: 'category'; 479 | }; 480 | options: { 481 | draftAndPublish: false; 482 | }; 483 | attributes: { 484 | articles: Schema.Attribute.Relation<'oneToMany', 'api::article.article'>; 485 | createdAt: Schema.Attribute.DateTime; 486 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 487 | Schema.Attribute.Private; 488 | description: Schema.Attribute.Text; 489 | locale: Schema.Attribute.String & Schema.Attribute.Private; 490 | localizations: Schema.Attribute.Relation< 491 | 'oneToMany', 492 | 'api::category.category' 493 | > & 494 | Schema.Attribute.Private; 495 | name: Schema.Attribute.String; 496 | publishedAt: Schema.Attribute.DateTime; 497 | slug: Schema.Attribute.UID; 498 | updatedAt: Schema.Attribute.DateTime; 499 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 500 | Schema.Attribute.Private; 501 | }; 502 | } 503 | 504 | export interface ApiGlobalGlobal extends Struct.SingleTypeSchema { 505 | collectionName: 'globals'; 506 | info: { 507 | description: 'Define global settings'; 508 | displayName: 'Global'; 509 | pluralName: 'globals'; 510 | singularName: 'global'; 511 | }; 512 | options: { 513 | draftAndPublish: false; 514 | }; 515 | attributes: { 516 | createdAt: Schema.Attribute.DateTime; 517 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 518 | Schema.Attribute.Private; 519 | defaultSeo: Schema.Attribute.Component<'shared.seo', false>; 520 | favicon: Schema.Attribute.Media<'images' | 'files' | 'videos'>; 521 | locale: Schema.Attribute.String & Schema.Attribute.Private; 522 | localizations: Schema.Attribute.Relation< 523 | 'oneToMany', 524 | 'api::global.global' 525 | > & 526 | Schema.Attribute.Private; 527 | publishedAt: Schema.Attribute.DateTime; 528 | siteDescription: Schema.Attribute.Text & Schema.Attribute.Required; 529 | siteName: Schema.Attribute.String & Schema.Attribute.Required; 530 | updatedAt: Schema.Attribute.DateTime; 531 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 532 | Schema.Attribute.Private; 533 | }; 534 | } 535 | 536 | export interface ApiPostPost extends Struct.CollectionTypeSchema { 537 | collectionName: 'posts'; 538 | info: { 539 | description: ''; 540 | displayName: 'Post'; 541 | pluralName: 'posts'; 542 | singularName: 'post'; 543 | }; 544 | options: { 545 | draftAndPublish: true; 546 | }; 547 | attributes: { 548 | author: Schema.Attribute.String; 549 | content: Schema.Attribute.RichText; 550 | createdAt: Schema.Attribute.DateTime; 551 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 552 | Schema.Attribute.Private; 553 | date: Schema.Attribute.Date; 554 | excerpt: Schema.Attribute.String; 555 | featured_image: Schema.Attribute.Media<'images'>; 556 | locale: Schema.Attribute.String & Schema.Attribute.Private; 557 | localizations: Schema.Attribute.Relation<'oneToMany', 'api::post.post'> & 558 | Schema.Attribute.Private; 559 | post_number: Schema.Attribute.Integer; 560 | publishedAt: Schema.Attribute.DateTime; 561 | slug: Schema.Attribute.UID<'title'>; 562 | title: Schema.Attribute.String; 563 | updatedAt: Schema.Attribute.DateTime; 564 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 565 | Schema.Attribute.Private; 566 | }; 567 | } 568 | 569 | export interface PluginContentReleasesRelease 570 | extends Struct.CollectionTypeSchema { 571 | collectionName: 'strapi_releases'; 572 | info: { 573 | displayName: 'Release'; 574 | pluralName: 'releases'; 575 | singularName: 'release'; 576 | }; 577 | options: { 578 | draftAndPublish: false; 579 | }; 580 | pluginOptions: { 581 | 'content-manager': { 582 | visible: false; 583 | }; 584 | 'content-type-builder': { 585 | visible: false; 586 | }; 587 | }; 588 | attributes: { 589 | actions: Schema.Attribute.Relation< 590 | 'oneToMany', 591 | 'plugin::content-releases.release-action' 592 | >; 593 | createdAt: Schema.Attribute.DateTime; 594 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 595 | Schema.Attribute.Private; 596 | locale: Schema.Attribute.String & Schema.Attribute.Private; 597 | localizations: Schema.Attribute.Relation< 598 | 'oneToMany', 599 | 'plugin::content-releases.release' 600 | > & 601 | Schema.Attribute.Private; 602 | name: Schema.Attribute.String & Schema.Attribute.Required; 603 | publishedAt: Schema.Attribute.DateTime; 604 | releasedAt: Schema.Attribute.DateTime; 605 | scheduledAt: Schema.Attribute.DateTime; 606 | status: Schema.Attribute.Enumeration< 607 | ['ready', 'blocked', 'failed', 'done', 'empty'] 608 | > & 609 | Schema.Attribute.Required; 610 | timezone: Schema.Attribute.String; 611 | updatedAt: Schema.Attribute.DateTime; 612 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 613 | Schema.Attribute.Private; 614 | }; 615 | } 616 | 617 | export interface PluginContentReleasesReleaseAction 618 | extends Struct.CollectionTypeSchema { 619 | collectionName: 'strapi_release_actions'; 620 | info: { 621 | displayName: 'Release Action'; 622 | pluralName: 'release-actions'; 623 | singularName: 'release-action'; 624 | }; 625 | options: { 626 | draftAndPublish: false; 627 | }; 628 | pluginOptions: { 629 | 'content-manager': { 630 | visible: false; 631 | }; 632 | 'content-type-builder': { 633 | visible: false; 634 | }; 635 | }; 636 | attributes: { 637 | contentType: Schema.Attribute.String & Schema.Attribute.Required; 638 | createdAt: Schema.Attribute.DateTime; 639 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 640 | Schema.Attribute.Private; 641 | entryDocumentId: Schema.Attribute.String; 642 | isEntryValid: Schema.Attribute.Boolean; 643 | locale: Schema.Attribute.String & Schema.Attribute.Private; 644 | localizations: Schema.Attribute.Relation< 645 | 'oneToMany', 646 | 'plugin::content-releases.release-action' 647 | > & 648 | Schema.Attribute.Private; 649 | publishedAt: Schema.Attribute.DateTime; 650 | release: Schema.Attribute.Relation< 651 | 'manyToOne', 652 | 'plugin::content-releases.release' 653 | >; 654 | type: Schema.Attribute.Enumeration<['publish', 'unpublish']> & 655 | Schema.Attribute.Required; 656 | updatedAt: Schema.Attribute.DateTime; 657 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 658 | Schema.Attribute.Private; 659 | }; 660 | } 661 | 662 | export interface PluginI18NLocale extends Struct.CollectionTypeSchema { 663 | collectionName: 'i18n_locale'; 664 | info: { 665 | collectionName: 'locales'; 666 | description: ''; 667 | displayName: 'Locale'; 668 | pluralName: 'locales'; 669 | singularName: 'locale'; 670 | }; 671 | options: { 672 | draftAndPublish: false; 673 | }; 674 | pluginOptions: { 675 | 'content-manager': { 676 | visible: false; 677 | }; 678 | 'content-type-builder': { 679 | visible: false; 680 | }; 681 | }; 682 | attributes: { 683 | code: Schema.Attribute.String & Schema.Attribute.Unique; 684 | createdAt: Schema.Attribute.DateTime; 685 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 686 | Schema.Attribute.Private; 687 | locale: Schema.Attribute.String & Schema.Attribute.Private; 688 | localizations: Schema.Attribute.Relation< 689 | 'oneToMany', 690 | 'plugin::i18n.locale' 691 | > & 692 | Schema.Attribute.Private; 693 | name: Schema.Attribute.String & 694 | Schema.Attribute.SetMinMax< 695 | { 696 | max: 50; 697 | min: 1; 698 | }, 699 | number 700 | >; 701 | publishedAt: Schema.Attribute.DateTime; 702 | updatedAt: Schema.Attribute.DateTime; 703 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 704 | Schema.Attribute.Private; 705 | }; 706 | } 707 | 708 | export interface PluginReviewWorkflowsWorkflow 709 | extends Struct.CollectionTypeSchema { 710 | collectionName: 'strapi_workflows'; 711 | info: { 712 | description: ''; 713 | displayName: 'Workflow'; 714 | name: 'Workflow'; 715 | pluralName: 'workflows'; 716 | singularName: 'workflow'; 717 | }; 718 | options: { 719 | draftAndPublish: false; 720 | }; 721 | pluginOptions: { 722 | 'content-manager': { 723 | visible: false; 724 | }; 725 | 'content-type-builder': { 726 | visible: false; 727 | }; 728 | }; 729 | attributes: { 730 | contentTypes: Schema.Attribute.JSON & 731 | Schema.Attribute.Required & 732 | Schema.Attribute.DefaultTo<'[]'>; 733 | createdAt: Schema.Attribute.DateTime; 734 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 735 | Schema.Attribute.Private; 736 | locale: Schema.Attribute.String & Schema.Attribute.Private; 737 | localizations: Schema.Attribute.Relation< 738 | 'oneToMany', 739 | 'plugin::review-workflows.workflow' 740 | > & 741 | Schema.Attribute.Private; 742 | name: Schema.Attribute.String & 743 | Schema.Attribute.Required & 744 | Schema.Attribute.Unique; 745 | publishedAt: Schema.Attribute.DateTime; 746 | stageRequiredToPublish: Schema.Attribute.Relation< 747 | 'oneToOne', 748 | 'plugin::review-workflows.workflow-stage' 749 | >; 750 | stages: Schema.Attribute.Relation< 751 | 'oneToMany', 752 | 'plugin::review-workflows.workflow-stage' 753 | >; 754 | updatedAt: Schema.Attribute.DateTime; 755 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 756 | Schema.Attribute.Private; 757 | }; 758 | } 759 | 760 | export interface PluginReviewWorkflowsWorkflowStage 761 | extends Struct.CollectionTypeSchema { 762 | collectionName: 'strapi_workflows_stages'; 763 | info: { 764 | description: ''; 765 | displayName: 'Stages'; 766 | name: 'Workflow Stage'; 767 | pluralName: 'workflow-stages'; 768 | singularName: 'workflow-stage'; 769 | }; 770 | options: { 771 | draftAndPublish: false; 772 | version: '1.1.0'; 773 | }; 774 | pluginOptions: { 775 | 'content-manager': { 776 | visible: false; 777 | }; 778 | 'content-type-builder': { 779 | visible: false; 780 | }; 781 | }; 782 | attributes: { 783 | color: Schema.Attribute.String & Schema.Attribute.DefaultTo<'#4945FF'>; 784 | createdAt: Schema.Attribute.DateTime; 785 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 786 | Schema.Attribute.Private; 787 | locale: Schema.Attribute.String & Schema.Attribute.Private; 788 | localizations: Schema.Attribute.Relation< 789 | 'oneToMany', 790 | 'plugin::review-workflows.workflow-stage' 791 | > & 792 | Schema.Attribute.Private; 793 | name: Schema.Attribute.String; 794 | permissions: Schema.Attribute.Relation<'manyToMany', 'admin::permission'>; 795 | publishedAt: Schema.Attribute.DateTime; 796 | updatedAt: Schema.Attribute.DateTime; 797 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 798 | Schema.Attribute.Private; 799 | workflow: Schema.Attribute.Relation< 800 | 'manyToOne', 801 | 'plugin::review-workflows.workflow' 802 | >; 803 | }; 804 | } 805 | 806 | export interface PluginUploadFile extends Struct.CollectionTypeSchema { 807 | collectionName: 'files'; 808 | info: { 809 | description: ''; 810 | displayName: 'File'; 811 | pluralName: 'files'; 812 | singularName: 'file'; 813 | }; 814 | options: { 815 | draftAndPublish: false; 816 | }; 817 | pluginOptions: { 818 | 'content-manager': { 819 | visible: false; 820 | }; 821 | 'content-type-builder': { 822 | visible: false; 823 | }; 824 | }; 825 | attributes: { 826 | alternativeText: Schema.Attribute.String; 827 | caption: Schema.Attribute.String; 828 | createdAt: Schema.Attribute.DateTime; 829 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 830 | Schema.Attribute.Private; 831 | ext: Schema.Attribute.String; 832 | folder: Schema.Attribute.Relation<'manyToOne', 'plugin::upload.folder'> & 833 | Schema.Attribute.Private; 834 | folderPath: Schema.Attribute.String & 835 | Schema.Attribute.Required & 836 | Schema.Attribute.Private & 837 | Schema.Attribute.SetMinMaxLength<{ 838 | minLength: 1; 839 | }>; 840 | formats: Schema.Attribute.JSON; 841 | hash: Schema.Attribute.String & Schema.Attribute.Required; 842 | height: Schema.Attribute.Integer; 843 | locale: Schema.Attribute.String & Schema.Attribute.Private; 844 | localizations: Schema.Attribute.Relation< 845 | 'oneToMany', 846 | 'plugin::upload.file' 847 | > & 848 | Schema.Attribute.Private; 849 | mime: Schema.Attribute.String & Schema.Attribute.Required; 850 | name: Schema.Attribute.String & Schema.Attribute.Required; 851 | previewUrl: Schema.Attribute.String; 852 | provider: Schema.Attribute.String & Schema.Attribute.Required; 853 | provider_metadata: Schema.Attribute.JSON; 854 | publishedAt: Schema.Attribute.DateTime; 855 | related: Schema.Attribute.Relation<'morphToMany'>; 856 | size: Schema.Attribute.Decimal & Schema.Attribute.Required; 857 | updatedAt: Schema.Attribute.DateTime; 858 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 859 | Schema.Attribute.Private; 860 | url: Schema.Attribute.String & Schema.Attribute.Required; 861 | width: Schema.Attribute.Integer; 862 | }; 863 | } 864 | 865 | export interface PluginUploadFolder extends Struct.CollectionTypeSchema { 866 | collectionName: 'upload_folders'; 867 | info: { 868 | displayName: 'Folder'; 869 | pluralName: 'folders'; 870 | singularName: 'folder'; 871 | }; 872 | options: { 873 | draftAndPublish: false; 874 | }; 875 | pluginOptions: { 876 | 'content-manager': { 877 | visible: false; 878 | }; 879 | 'content-type-builder': { 880 | visible: false; 881 | }; 882 | }; 883 | attributes: { 884 | children: Schema.Attribute.Relation<'oneToMany', 'plugin::upload.folder'>; 885 | createdAt: Schema.Attribute.DateTime; 886 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 887 | Schema.Attribute.Private; 888 | files: Schema.Attribute.Relation<'oneToMany', 'plugin::upload.file'>; 889 | locale: Schema.Attribute.String & Schema.Attribute.Private; 890 | localizations: Schema.Attribute.Relation< 891 | 'oneToMany', 892 | 'plugin::upload.folder' 893 | > & 894 | Schema.Attribute.Private; 895 | name: Schema.Attribute.String & 896 | Schema.Attribute.Required & 897 | Schema.Attribute.SetMinMaxLength<{ 898 | minLength: 1; 899 | }>; 900 | parent: Schema.Attribute.Relation<'manyToOne', 'plugin::upload.folder'>; 901 | path: Schema.Attribute.String & 902 | Schema.Attribute.Required & 903 | Schema.Attribute.SetMinMaxLength<{ 904 | minLength: 1; 905 | }>; 906 | pathId: Schema.Attribute.Integer & 907 | Schema.Attribute.Required & 908 | Schema.Attribute.Unique; 909 | publishedAt: Schema.Attribute.DateTime; 910 | updatedAt: Schema.Attribute.DateTime; 911 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 912 | Schema.Attribute.Private; 913 | }; 914 | } 915 | 916 | export interface PluginUsersPermissionsPermission 917 | extends Struct.CollectionTypeSchema { 918 | collectionName: 'up_permissions'; 919 | info: { 920 | description: ''; 921 | displayName: 'Permission'; 922 | name: 'permission'; 923 | pluralName: 'permissions'; 924 | singularName: 'permission'; 925 | }; 926 | options: { 927 | draftAndPublish: false; 928 | }; 929 | pluginOptions: { 930 | 'content-manager': { 931 | visible: false; 932 | }; 933 | 'content-type-builder': { 934 | visible: false; 935 | }; 936 | }; 937 | attributes: { 938 | action: Schema.Attribute.String & Schema.Attribute.Required; 939 | createdAt: Schema.Attribute.DateTime; 940 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 941 | Schema.Attribute.Private; 942 | locale: Schema.Attribute.String & Schema.Attribute.Private; 943 | localizations: Schema.Attribute.Relation< 944 | 'oneToMany', 945 | 'plugin::users-permissions.permission' 946 | > & 947 | Schema.Attribute.Private; 948 | publishedAt: Schema.Attribute.DateTime; 949 | role: Schema.Attribute.Relation< 950 | 'manyToOne', 951 | 'plugin::users-permissions.role' 952 | >; 953 | updatedAt: Schema.Attribute.DateTime; 954 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 955 | Schema.Attribute.Private; 956 | }; 957 | } 958 | 959 | export interface PluginUsersPermissionsRole 960 | extends Struct.CollectionTypeSchema { 961 | collectionName: 'up_roles'; 962 | info: { 963 | description: ''; 964 | displayName: 'Role'; 965 | name: 'role'; 966 | pluralName: 'roles'; 967 | singularName: 'role'; 968 | }; 969 | options: { 970 | draftAndPublish: false; 971 | }; 972 | pluginOptions: { 973 | 'content-manager': { 974 | visible: false; 975 | }; 976 | 'content-type-builder': { 977 | visible: false; 978 | }; 979 | }; 980 | attributes: { 981 | createdAt: Schema.Attribute.DateTime; 982 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 983 | Schema.Attribute.Private; 984 | description: Schema.Attribute.String; 985 | locale: Schema.Attribute.String & Schema.Attribute.Private; 986 | localizations: Schema.Attribute.Relation< 987 | 'oneToMany', 988 | 'plugin::users-permissions.role' 989 | > & 990 | Schema.Attribute.Private; 991 | name: Schema.Attribute.String & 992 | Schema.Attribute.Required & 993 | Schema.Attribute.SetMinMaxLength<{ 994 | minLength: 3; 995 | }>; 996 | permissions: Schema.Attribute.Relation< 997 | 'oneToMany', 998 | 'plugin::users-permissions.permission' 999 | >; 1000 | publishedAt: Schema.Attribute.DateTime; 1001 | type: Schema.Attribute.String & Schema.Attribute.Unique; 1002 | updatedAt: Schema.Attribute.DateTime; 1003 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 1004 | Schema.Attribute.Private; 1005 | users: Schema.Attribute.Relation< 1006 | 'oneToMany', 1007 | 'plugin::users-permissions.user' 1008 | >; 1009 | }; 1010 | } 1011 | 1012 | export interface PluginUsersPermissionsUser 1013 | extends Struct.CollectionTypeSchema { 1014 | collectionName: 'up_users'; 1015 | info: { 1016 | description: ''; 1017 | displayName: 'User'; 1018 | name: 'user'; 1019 | pluralName: 'users'; 1020 | singularName: 'user'; 1021 | }; 1022 | options: { 1023 | draftAndPublish: false; 1024 | timestamps: true; 1025 | }; 1026 | attributes: { 1027 | blocked: Schema.Attribute.Boolean & Schema.Attribute.DefaultTo; 1028 | confirmationToken: Schema.Attribute.String & Schema.Attribute.Private; 1029 | confirmed: Schema.Attribute.Boolean & Schema.Attribute.DefaultTo; 1030 | createdAt: Schema.Attribute.DateTime; 1031 | createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 1032 | Schema.Attribute.Private; 1033 | email: Schema.Attribute.Email & 1034 | Schema.Attribute.Required & 1035 | Schema.Attribute.SetMinMaxLength<{ 1036 | minLength: 6; 1037 | }>; 1038 | locale: Schema.Attribute.String & Schema.Attribute.Private; 1039 | localizations: Schema.Attribute.Relation< 1040 | 'oneToMany', 1041 | 'plugin::users-permissions.user' 1042 | > & 1043 | Schema.Attribute.Private; 1044 | password: Schema.Attribute.Password & 1045 | Schema.Attribute.Private & 1046 | Schema.Attribute.SetMinMaxLength<{ 1047 | minLength: 6; 1048 | }>; 1049 | provider: Schema.Attribute.String; 1050 | publishedAt: Schema.Attribute.DateTime; 1051 | resetPasswordToken: Schema.Attribute.String & Schema.Attribute.Private; 1052 | role: Schema.Attribute.Relation< 1053 | 'manyToOne', 1054 | 'plugin::users-permissions.role' 1055 | >; 1056 | updatedAt: Schema.Attribute.DateTime; 1057 | updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & 1058 | Schema.Attribute.Private; 1059 | username: Schema.Attribute.String & 1060 | Schema.Attribute.Required & 1061 | Schema.Attribute.Unique & 1062 | Schema.Attribute.SetMinMaxLength<{ 1063 | minLength: 3; 1064 | }>; 1065 | }; 1066 | } 1067 | 1068 | declare module '@strapi/strapi' { 1069 | export module Public { 1070 | export interface ContentTypeSchemas { 1071 | 'admin::api-token': AdminApiToken; 1072 | 'admin::api-token-permission': AdminApiTokenPermission; 1073 | 'admin::permission': AdminPermission; 1074 | 'admin::role': AdminRole; 1075 | 'admin::transfer-token': AdminTransferToken; 1076 | 'admin::transfer-token-permission': AdminTransferTokenPermission; 1077 | 'admin::user': AdminUser; 1078 | 'api::about.about': ApiAboutAbout; 1079 | 'api::article.article': ApiArticleArticle; 1080 | 'api::author.author': ApiAuthorAuthor; 1081 | 'api::category.category': ApiCategoryCategory; 1082 | 'api::global.global': ApiGlobalGlobal; 1083 | 'api::post.post': ApiPostPost; 1084 | 'plugin::content-releases.release': PluginContentReleasesRelease; 1085 | 'plugin::content-releases.release-action': PluginContentReleasesReleaseAction; 1086 | 'plugin::i18n.locale': PluginI18NLocale; 1087 | 'plugin::review-workflows.workflow': PluginReviewWorkflowsWorkflow; 1088 | 'plugin::review-workflows.workflow-stage': PluginReviewWorkflowsWorkflowStage; 1089 | 'plugin::upload.file': PluginUploadFile; 1090 | 'plugin::upload.folder': PluginUploadFolder; 1091 | 'plugin::users-permissions.permission': PluginUsersPermissionsPermission; 1092 | 'plugin::users-permissions.role': PluginUsersPermissionsRole; 1093 | 'plugin::users-permissions.user': PluginUsersPermissionsUser; 1094 | } 1095 | } 1096 | } 1097 | -------------------------------------------------------------------------------- /upload_posts.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const axios = require("axios"); 4 | const { format } = require("date-fns"); 5 | const dotenv = require('dotenv'); 6 | dotenv.config(); 7 | 8 | const STRAPI_URL = process.env.STRAPI_URL; 9 | const STRAPI_TOKEN = process.env.STRAPI_TOKEN; 10 | const WORDPRESS_API = process.env.WORDPRESS_API; 11 | const IMAGE_MAPPING_FILE = path.join(__dirname, "image_mappings.json"); 12 | 13 | let imageMappings = {}; 14 | if (fs.existsSync(IMAGE_MAPPING_FILE)) { 15 | imageMappings = JSON.parse(fs.readFileSync(IMAGE_MAPPING_FILE, "utf8")); 16 | } 17 | 18 | async function fetchWordPressPosts() { 19 | try { 20 | console.log("📡 Fetching posts from WordPress API..."); 21 | const response = await axios.get(WORDPRESS_API); 22 | console.log(`✅ Found ${response.data.length} posts.`); 23 | return response.data; 24 | } catch (error) { 25 | console.error("❌ Error fetching WordPress posts:", error.message); 26 | process.exit(1); 27 | } 28 | } 29 | 30 | async function savePostToStrapi(post) { 31 | try { 32 | console.log(`📝 Saving post: ${post.title}...`); 33 | 34 | let featuredImageId = null; 35 | if (post.featured_image) { 36 | const imageName = path.basename(post.featured_image); 37 | if (imageMappings[imageName]) { 38 | featuredImageId = imageMappings[imageName]; 39 | } else { 40 | console.log(`❌ Image not found for: ${post.title}`); 41 | } 42 | } 43 | 44 | const formattedDate = format(new Date(post.date), "yyyy-MM-dd"); 45 | 46 | const postData = { 47 | data: { 48 | post_number: post.post_number, 49 | title: post.title, 50 | content: post.content, 51 | slug: post.slug, 52 | excerpt: post.excerpt, 53 | author: post.author, 54 | date: formattedDate, 55 | featured_image: featuredImageId ? { id: featuredImageId } : null, 56 | }, 57 | }; 58 | 59 | try { 60 | const createResponse = await axios.post(STRAPI_URL, postData, { 61 | headers: { 62 | Authorization: `Bearer ${STRAPI_TOKEN}`, 63 | "Content-Type": "application/json", 64 | }, 65 | }); 66 | console.log(`✅ Post saved: ${createResponse.data.data.id}`); 67 | } catch (error) { 68 | if ( 69 | error.response && 70 | error.response.data && 71 | error.response.data.error && 72 | error.response.data.error.message === "This attribute must be unique" 73 | ) { 74 | console.log(`⚠️ Post with slug "${post.slug}" already exists, skipping.`); 75 | } else { 76 | console.error("❌ Error saving post:", error.response ? error.response.data : error.message); 77 | } 78 | } 79 | } catch (error) { 80 | console.error("❌ Error processing post:", error.response ? error.response.data : error.message); 81 | } 82 | } 83 | 84 | 85 | 86 | 87 | async function migratePosts() { 88 | const posts = await fetchWordPressPosts(); 89 | for (const post of posts) { 90 | await savePostToStrapi(post); 91 | } 92 | console.log("🎉 Migration completed!"); 93 | } 94 | 95 | // Please Run upload_assets script before running this script to upload posts media 96 | migratePosts(); 97 | --------------------------------------------------------------------------------