Compare commits
12 Commits
v0.1.7
...
ec64a42c2e
| Author | SHA1 | Date | |
|---|---|---|---|
| ec64a42c2e | |||
| 5f9c29c39a | |||
| 27f4f73148 | |||
| 21a7036ef5 | |||
| b1b1aa47c9 | |||
| 54fea5f64a | |||
| a27e6af5db | |||
| 2c86905c91 | |||
| 065b729a2f | |||
| c82bf47e98 | |||
| 51f594baa5 | |||
| 3e56ba7eb3 |
20
wp-content/mu-plugins/headless-home-url.php
Normal file
20
wp-content/mu-plugins/headless-home-url.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
// Customize home URL for headless WordPress
|
||||
add_filter( 'home_url', 'headless_home_url', 10, 4 );
|
||||
function headless_home_url( $url, $path, $orig_scheme, $blog_id ) {
|
||||
// Exclude specific patterns from rewriting
|
||||
$excluded_patterns = array(
|
||||
'#/wp-json(/|$)#i', // WP REST API
|
||||
'#\.(xsl|xml)$#i', // Sitemap and XSLT files
|
||||
);
|
||||
foreach ( $excluded_patterns as $pattern ) {
|
||||
if ( preg_match( $pattern, $url ) ) {
|
||||
return get_site_url( $blog_id, $path, $orig_scheme );
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite URL protocol to match original home scheme
|
||||
$scheme = wp_parse_url( get_option( 'home' ) )['scheme'] ?? 'https';
|
||||
return preg_replace( '#^https:#i', "$scheme:", $url );
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core";
|
||||
|
||||
export function useResponsive() {
|
||||
const { isMobileOrTablet } = useDevice();
|
||||
const breakpoints = useBreakpoints(breakpointsTailwind, { ssrWidth: isMobileOrTablet ? 375 : 1024 });
|
||||
const isDesktop = breakpoints.greaterOrEqual("lg");
|
||||
|
||||
return { breakpoints, isDesktop };
|
||||
}
|
||||
@@ -5,4 +5,9 @@ require_once __DIR__ . '/includes/core/theme-setup.php';
|
||||
|
||||
// Vendors
|
||||
require_once __DIR__ . '/includes/vendors/acf.php';
|
||||
require_once __DIR__ . '/includes/vendors/rankmath.php';
|
||||
require_once __DIR__ . '/includes/vendors/tinymce.php';
|
||||
require_once __DIR__ . '/includes/vendors/wpgraphql.php';
|
||||
|
||||
// WPGraphQL
|
||||
require_once __DIR__ . '/includes/wpgraphql/term-connection.php';
|
||||
|
||||
@@ -17,21 +17,6 @@ function moonshine_after_setup_theme() {
|
||||
// Register sidebars
|
||||
}
|
||||
|
||||
// Bypass headless home URL for specific cases
|
||||
add_filter( 'home_url', 'moonshine_bypass_home_url', 10, 4 );
|
||||
function moonshine_bypass_home_url( $url, $path, $orig_scheme, $blog_id ) {
|
||||
$excluded_patterns = array(
|
||||
'#/wp-json(/|$)#i', // WP REST API
|
||||
'#\.(xsl|xml)$#i', // Sitemap and XSLT files
|
||||
);
|
||||
foreach ( $excluded_patterns as $pattern ) {
|
||||
if ( preg_match( $pattern, $url ) ) {
|
||||
return get_site_url( $blog_id, $path, $orig_scheme );
|
||||
}
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
// Display theme version in admin footer
|
||||
add_filter( 'update_footer', 'moonshine_update_footer', 100 );
|
||||
function moonshine_update_footer() {
|
||||
|
||||
1
wp-content/themes/moonshine/includes/vendors/rankmath.php
vendored
Normal file
1
wp-content/themes/moonshine/includes/vendors/rankmath.php
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<?php
|
||||
17
wp-content/themes/moonshine/includes/vendors/wpgraphql.php
vendored
Normal file
17
wp-content/themes/moonshine/includes/vendors/wpgraphql.php
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
// Default WPGraphQL settings
|
||||
add_filter( 'graphql_get_setting_section_field_value', 'moonshine_wpgraphql_settings', 10, 5 );
|
||||
function moonshine_wpgraphql_settings( $value, $default_value, $option_name, $section_fields, $section_name ) {
|
||||
if ( $section_name === 'graphql_general_settings' ) {
|
||||
switch ( $option_name ) {
|
||||
case 'graphql_endpoint':
|
||||
$value = 'graphql';
|
||||
break;
|
||||
case "public_introspection_enabled":
|
||||
$value = "on";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
// Override TermConnection query args
|
||||
add_filter( 'graphql_term_object_connection_query_args', 'moonshine_graphql_term_object_connection_query_args', 10, 3 );
|
||||
function moonshine_graphql_term_object_connection_query_args( $query_args, $source, $args ) {
|
||||
// Sort by 'order' meta value instead of legacy 'term_order' field
|
||||
if ( 'term_order' === $args['where']['orderby'] ?? false ) {
|
||||
$query_args['meta_key'] = 'order';
|
||||
$query_args['orderby'] = 'meta_value_num';
|
||||
}
|
||||
|
||||
return $query_args;
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { version } from "./package.json";
|
||||
|
||||
const siteUrl = process.env.NUXT_SITE_URL;
|
||||
if (!siteUrl) {
|
||||
throw new Error(`NUXT_SITE_URL is not defined. Make sure to set it in your build environment variables.`);
|
||||
@@ -15,8 +17,10 @@ export default defineNuxtConfig({
|
||||
"@lewebsimple/nuxt-graphql",
|
||||
"@nuxt/eslint",
|
||||
"@nuxt/ui",
|
||||
"@nuxtjs/device",
|
||||
"@nuxtjs/seo",
|
||||
"nuxt-auth-utils",
|
||||
"nuxt-svgo",
|
||||
],
|
||||
|
||||
components: {
|
||||
@@ -39,6 +43,10 @@ export default defineNuxtConfig({
|
||||
colorMode: false,
|
||||
},
|
||||
|
||||
runtimeConfig: {
|
||||
wpUrl,
|
||||
},
|
||||
|
||||
compatibilityDate: "2026-01-01",
|
||||
|
||||
nitro: {
|
||||
@@ -47,13 +55,24 @@ export default defineNuxtConfig({
|
||||
deployConfig: true,
|
||||
nodeCompat: true,
|
||||
wrangler: {
|
||||
name: "foobar",
|
||||
// Project name
|
||||
name: "moonshine",
|
||||
// Cloudflare Workers settings
|
||||
compatibility_date: "2026-01-27",
|
||||
main: "./.output/server/index.mjs",
|
||||
observability: { enabled: true },
|
||||
preview_urls: false,
|
||||
// Environment variables
|
||||
vars: {
|
||||
NODE_ENV: "production",
|
||||
NODE_ENV: "staging",
|
||||
NUXT_SITE_URL: siteUrl,
|
||||
NUXT_WP_URL: wpUrl,
|
||||
},
|
||||
// Bindings
|
||||
assets: {
|
||||
binding: "ASSETS",
|
||||
directory: "./.output/public/",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -71,6 +90,11 @@ export default defineNuxtConfig({
|
||||
},
|
||||
|
||||
graphql: {
|
||||
client: {
|
||||
cache: {
|
||||
keyVersion: version,
|
||||
},
|
||||
},
|
||||
server: {
|
||||
context: ["server/graphql/context"],
|
||||
schema: {
|
||||
@@ -85,4 +109,10 @@ export default defineNuxtConfig({
|
||||
|
||||
sitemap: false,
|
||||
|
||||
svgo: {
|
||||
autoImportPath: "~/assets/svg/",
|
||||
componentPrefix: "Svg",
|
||||
defaultImport: "component",
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify-json/lucide": "^1.2.87",
|
||||
"@lewebsimple/nuxt-graphql": "^0.6.1",
|
||||
"@lewebsimple/nuxt-graphql": "^0.6.6",
|
||||
"@nuxt/ui": "4.3.0",
|
||||
"@nuxtjs/seo": "^3.3.0",
|
||||
"@nuxtjs/device": "4.0.0",
|
||||
"@nuxtjs/seo": "^3.4.0",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"nuxt": "^4.3.0",
|
||||
"nuxt-auth-utils": "^0.5.28",
|
||||
"nuxt-svgo": "^4.2.6",
|
||||
"tailwindcss": "^4.1.18",
|
||||
"vue": "^3.5.27",
|
||||
"vue-router": "^4.6.4",
|
||||
|
||||
534
wp-content/themes/moonshine/pnpm-lock.yaml
generated
534
wp-content/themes/moonshine/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -3250,6 +3250,19 @@ type GeneralSettings {
|
||||
"""Code local de l’installation WordPress."""
|
||||
language: String
|
||||
|
||||
"""
|
||||
The media item representing the site icon configured in site settings, used as the site's favicon and app icon.
|
||||
"""
|
||||
siteIcon: GeneralSettingsToMediaItemConnectionEdge
|
||||
|
||||
"""
|
||||
Site icon URL configured in site settings, used as the site's favicon and app icon.
|
||||
"""
|
||||
siteIconUrl(
|
||||
"""Size of the site icon in pixels. Defaults to 512. Max 512."""
|
||||
size: Int
|
||||
): String
|
||||
|
||||
"""
|
||||
Le numéro du jour de la semaine à laquelle la semaine devrait commencer.
|
||||
"""
|
||||
@@ -3268,6 +3281,17 @@ type GeneralSettings {
|
||||
url: String
|
||||
}
|
||||
|
||||
"""Connection between the GeneralSettings type and the MediaItem type"""
|
||||
type GeneralSettingsToMediaItemConnectionEdge implements Edge & MediaItemConnectionEdge & OneToOneConnection {
|
||||
"""
|
||||
Opaque reference to the nodes position in the connection. Value can be used with pagination args.
|
||||
"""
|
||||
cursor: String
|
||||
|
||||
"""The node of the connection, without the edges"""
|
||||
node: MediaItem!
|
||||
}
|
||||
|
||||
"""The Login client options for the github provider."""
|
||||
type GithubClientOptions implements LoginClientOptions {
|
||||
"""The client ID."""
|
||||
@@ -9478,6 +9502,18 @@ type RankMathAuthorArchiveMetaSettings implements RankMathMetaSettingWithArchive
|
||||
robotsMeta: [RankMathRobotsMetaValueEnum]
|
||||
}
|
||||
|
||||
"""The Breadcrumb trail."""
|
||||
type RankMathBreadcrumbs {
|
||||
"""Whether the given breadcrumb is hidden from the schema"""
|
||||
isHidden: Boolean
|
||||
|
||||
"""The text for the given breadcrumb"""
|
||||
text: String
|
||||
|
||||
"""The url for the given breadcrumb"""
|
||||
url: String
|
||||
}
|
||||
|
||||
"""The RankMath SEO breadcrumbs settings."""
|
||||
type RankMathBreadcrumbsConfig {
|
||||
"""Format the label used for archive pages."""
|
||||
@@ -9533,6 +9569,9 @@ type RankMathCategoryTermSeo implements RankMathSeo {
|
||||
"""The title to use in the breadcrumbs for this post"""
|
||||
breadcrumbTitle: String
|
||||
|
||||
"""The breadcrumbs trail for the given object"""
|
||||
breadcrumbs: [RankMathBreadcrumbs]
|
||||
|
||||
"""The canonical url."""
|
||||
canonicalUrl: String
|
||||
|
||||
@@ -9563,6 +9602,9 @@ interface RankMathContentNodeSeo implements RankMathSeo {
|
||||
"""The title to use in the breadcrumbs for this post"""
|
||||
breadcrumbTitle: String
|
||||
|
||||
"""The breadcrumbs trail for the given object"""
|
||||
breadcrumbs: [RankMathBreadcrumbs]
|
||||
|
||||
"""The canonical url."""
|
||||
canonicalUrl: String
|
||||
|
||||
@@ -9825,6 +9867,9 @@ type RankMathMediaItemObjectSeo implements RankMathContentNodeSeo & RankMathSeo
|
||||
"""The title to use in the breadcrumbs for this post"""
|
||||
breadcrumbTitle: String
|
||||
|
||||
"""The breadcrumbs trail for the given object"""
|
||||
breadcrumbs: [RankMathBreadcrumbs]
|
||||
|
||||
"""The canonical url."""
|
||||
canonicalUrl: String
|
||||
|
||||
@@ -9861,6 +9906,9 @@ type RankMathMediaItemTypeSeo implements RankMathSeo {
|
||||
"""The title to use in the breadcrumbs for this post"""
|
||||
breadcrumbTitle: String
|
||||
|
||||
"""The breadcrumbs trail for the given object"""
|
||||
breadcrumbs: [RankMathBreadcrumbs]
|
||||
|
||||
"""The canonical url."""
|
||||
canonicalUrl: String
|
||||
|
||||
@@ -10602,6 +10650,9 @@ type RankMathPageObjectSeo implements RankMathContentNodeSeo & RankMathSeo {
|
||||
"""The title to use in the breadcrumbs for this post"""
|
||||
breadcrumbTitle: String
|
||||
|
||||
"""The breadcrumbs trail for the given object"""
|
||||
breadcrumbs: [RankMathBreadcrumbs]
|
||||
|
||||
"""The canonical url."""
|
||||
canonicalUrl: String
|
||||
|
||||
@@ -10638,6 +10689,9 @@ type RankMathPageTypeSeo implements RankMathSeo {
|
||||
"""The title to use in the breadcrumbs for this post"""
|
||||
breadcrumbTitle: String
|
||||
|
||||
"""The breadcrumbs trail for the given object"""
|
||||
breadcrumbs: [RankMathBreadcrumbs]
|
||||
|
||||
"""The canonical url."""
|
||||
canonicalUrl: String
|
||||
|
||||
@@ -10668,6 +10722,9 @@ type RankMathPostFormatTermSeo implements RankMathSeo {
|
||||
"""The title to use in the breadcrumbs for this post"""
|
||||
breadcrumbTitle: String
|
||||
|
||||
"""The breadcrumbs trail for the given object"""
|
||||
breadcrumbs: [RankMathBreadcrumbs]
|
||||
|
||||
"""The canonical url."""
|
||||
canonicalUrl: String
|
||||
|
||||
@@ -10698,6 +10755,9 @@ type RankMathPostObjectSeo implements RankMathContentNodeSeo & RankMathSeo {
|
||||
"""The title to use in the breadcrumbs for this post"""
|
||||
breadcrumbTitle: String
|
||||
|
||||
"""The breadcrumbs trail for the given object"""
|
||||
breadcrumbs: [RankMathBreadcrumbs]
|
||||
|
||||
"""The canonical url."""
|
||||
canonicalUrl: String
|
||||
|
||||
@@ -10734,6 +10794,9 @@ type RankMathPostTypeSeo implements RankMathSeo {
|
||||
"""The title to use in the breadcrumbs for this post"""
|
||||
breadcrumbTitle: String
|
||||
|
||||
"""The breadcrumbs trail for the given object"""
|
||||
breadcrumbs: [RankMathBreadcrumbs]
|
||||
|
||||
"""The canonical url."""
|
||||
canonicalUrl: String
|
||||
|
||||
@@ -10806,6 +10869,9 @@ interface RankMathSeo {
|
||||
"""The title to use in the breadcrumbs for this post"""
|
||||
breadcrumbTitle: String
|
||||
|
||||
"""The breadcrumbs trail for the given object"""
|
||||
breadcrumbs: [RankMathBreadcrumbs]
|
||||
|
||||
"""The canonical url."""
|
||||
canonicalUrl: String
|
||||
|
||||
@@ -11253,6 +11319,9 @@ type RankMathTagTermSeo implements RankMathSeo {
|
||||
"""The title to use in the breadcrumbs for this post"""
|
||||
breadcrumbTitle: String
|
||||
|
||||
"""The breadcrumbs trail for the given object"""
|
||||
breadcrumbs: [RankMathBreadcrumbs]
|
||||
|
||||
"""The canonical url."""
|
||||
canonicalUrl: String
|
||||
|
||||
@@ -11313,6 +11382,9 @@ type RankMathUserSeo implements RankMathSeo {
|
||||
"""The title to use in the breadcrumbs for this post"""
|
||||
breadcrumbTitle: String
|
||||
|
||||
"""The breadcrumbs trail for the given object"""
|
||||
breadcrumbs: [RankMathBreadcrumbs]
|
||||
|
||||
"""The canonical url."""
|
||||
canonicalUrl: String
|
||||
|
||||
@@ -15551,9 +15623,6 @@ enum UserRoleEnum {
|
||||
|
||||
"""User role with specific capabilities"""
|
||||
SUBSCRIBER
|
||||
|
||||
"""User role with specific capabilities"""
|
||||
TRANSLATOR
|
||||
}
|
||||
|
||||
"""Connection between the User type and the Comment type"""
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { defu } from "defu";
|
||||
|
||||
export default defineRemoteExecutorHooks({
|
||||
onRequest(request) {
|
||||
if (request.context.authToken) {
|
||||
request.extensions ??= {};
|
||||
request.extensions.headers = {
|
||||
...request.extensions.headers,
|
||||
Authorization: `Bearer ${request.context.authToken}`,
|
||||
};
|
||||
// Attach the Authorization header if an authToken is present in the context
|
||||
if (request.context?.authToken) {
|
||||
request.extensions = defu(request.extensions, { headers: { Authorization: `Bearer ${request.context.authToken}` } });
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -42,8 +42,8 @@ function getAuthUser(user: AuthUserFragment): User {
|
||||
|
||||
// Refresh auth token by calling remote GraphQL endpoint directly
|
||||
export async function refreshAuthToken(refreshToken: string): Promise<string | undefined> {
|
||||
// TODO: const { public: { graphql: { endpoint } } } = useRuntimeConfig();
|
||||
const endpoint = `${process.env.NUXT_WP_URL || "https://cultureat.ledevsimple.ca"}/graphql`;
|
||||
const { public: { wpUrl } } = useRuntimeConfig();
|
||||
const endpoint = `${wpUrl}/graphql`;
|
||||
const { data } = await executeGraphQLHTTP<ResultOf<"AuthRefreshToken">>({
|
||||
query: AuthRefreshTokenDocument,
|
||||
variables: { refreshToken },
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Helper: Extracts nodes from a GraphQL connection object, returning an empty array if nodes are absent.
|
||||
export function extractNodes<T>(connection: { nodes?: readonly T[] } | null | undefined): readonly T[] {
|
||||
export function extractNodes<T>(connection: { nodes?: T[] } | undefined): T[] {
|
||||
return connection?.nodes ?? [];
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* For more details on how to configure Wrangler, refer to:
|
||||
* https://developers.cloudflare.com/workers/wrangler/configuration/
|
||||
*/
|
||||
{
|
||||
"$schema": "node_modules/wrangler/config-schema.json",
|
||||
"main": "./.output/server/index.mjs",
|
||||
/**
|
||||
* Static Assets Binding
|
||||
*/
|
||||
"assets": {
|
||||
"binding": "ASSETS",
|
||||
"directory": "./.output/public/"
|
||||
},
|
||||
/**
|
||||
* Observability & Analytics
|
||||
*/
|
||||
"observability": {
|
||||
"enabled": true
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user