mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-16 20:22:15 +01:00
Add core support for extendable authentication
add support for passwordless user creation; add filament loading support for new laravel modules namespacing; add support for pluggable password reset and login rules
This commit is contained in:
@@ -7,10 +7,16 @@ import { Field, FieldLabel, FieldError } from '@/packages/ui/src/field';
|
||||
import PrimaryButton from '@/packages/ui/src/Buttons/PrimaryButton.vue';
|
||||
import TextInput from '@/packages/ui/src/Input/TextInput.vue';
|
||||
|
||||
defineProps({
|
||||
canResetPassword: Boolean,
|
||||
status: String,
|
||||
});
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
canResetPassword?: boolean;
|
||||
status?: string;
|
||||
}>(),
|
||||
{
|
||||
canResetPassword: false,
|
||||
status: '',
|
||||
}
|
||||
);
|
||||
|
||||
const form = useForm({
|
||||
email: '',
|
||||
@@ -28,8 +34,8 @@ const submit = () => {
|
||||
};
|
||||
|
||||
const page = usePage<{
|
||||
flash: {
|
||||
message: string;
|
||||
flash?: {
|
||||
message?: string;
|
||||
};
|
||||
}>();
|
||||
</script>
|
||||
@@ -61,6 +67,9 @@ const page = usePage<{
|
||||
{{ page.props.flash?.message }}
|
||||
</div>
|
||||
|
||||
<!-- Extension seam: alternative-auth errors (e.g. SSO callback failures) -->
|
||||
<slot name="error" />
|
||||
|
||||
<form @submit.prevent="submit">
|
||||
<Field>
|
||||
<FieldLabel for="email">Email</FieldLabel>
|
||||
@@ -103,5 +112,8 @@ const page = usePage<{
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Extension seam: alternative auth methods (e.g. SSO providers) -->
|
||||
<slot name="alternatives" />
|
||||
</AuthenticationCard>
|
||||
</template>
|
||||
|
||||
@@ -10,35 +10,73 @@ import { QueryClient, VueQueryPlugin } from '@tanstack/vue-query';
|
||||
import { type DefineComponent } from 'vue';
|
||||
import { setupPrefetching } from '@/utils/prefetch';
|
||||
|
||||
interface ExtensionManifest {
|
||||
name?: string;
|
||||
alias?: string;
|
||||
}
|
||||
|
||||
const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
|
||||
const pinia = createPinia();
|
||||
const queryClient = new QueryClient();
|
||||
const extensionManifests = import.meta.glob('../../extensions/**/module.json', {
|
||||
eager: true,
|
||||
import: 'default',
|
||||
}) as Record<string, ExtensionManifest>;
|
||||
// BillingPortal is a Vue 2 component and must not be bundled into the Vue 3 app.
|
||||
const extensionPages = import.meta.glob<DefineComponent>([
|
||||
'../../extensions/**/resources/js/Pages/**/*.vue',
|
||||
'!**/BillingPortal.vue',
|
||||
]);
|
||||
const extensionDirectories = Object.entries(extensionManifests).reduce<Record<string, string>>(
|
||||
(directories, [path, manifest]) => {
|
||||
const match = path.match(/^\.\.\/\.\.\/extensions\/([^/]+)\/module\.json$/);
|
||||
const extensionDirectory = match?.[1];
|
||||
|
||||
if (extensionDirectory === undefined) {
|
||||
return directories;
|
||||
}
|
||||
|
||||
for (const key of [manifest.name, manifest.alias, extensionDirectory]) {
|
||||
if (typeof key !== 'string' || key === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
directories[key] = extensionDirectory;
|
||||
directories[key.toLowerCase()] = extensionDirectory;
|
||||
}
|
||||
|
||||
return directories;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
function resolveExtensionDirectory(moduleName: string): string {
|
||||
return (
|
||||
extensionDirectories[moduleName] ??
|
||||
extensionDirectories[moduleName.toLowerCase()] ??
|
||||
moduleName
|
||||
);
|
||||
}
|
||||
|
||||
createInertiaApp({
|
||||
title: (title) => `${title} - ${appName}`,
|
||||
resolve: (name) => {
|
||||
if (name.includes('Invoicing::')) {
|
||||
const [module, page] = name.split('::');
|
||||
// "Module::Page" (both halves present) resolves to that extension's page
|
||||
// directory; everything else is a host page under resources/js/Pages.
|
||||
const [module, ...pageSegments] = name.split('::');
|
||||
const page = pageSegments.join('::');
|
||||
|
||||
const pagePath = module
|
||||
? `../../extensions/${module}/resources/js/Pages/${page}.vue`
|
||||
: `./Pages/${page}.vue`;
|
||||
if (module && page) {
|
||||
const extensionDirectory = resolveExtensionDirectory(module);
|
||||
const pagePath = `../../extensions/${extensionDirectory}/resources/js/Pages/${page}.vue`;
|
||||
|
||||
// BillingPortal is a Vue 2 Component and therefore should not be imported
|
||||
const pages = module
|
||||
? import.meta.glob<DefineComponent>([
|
||||
'../../extensions/**/resources/js/Pages/*.vue',
|
||||
'!**/BillingPortal.vue',
|
||||
])
|
||||
: import.meta.glob<DefineComponent>('./Pages/**/*.vue');
|
||||
|
||||
return resolvePageComponent(pagePath, pages);
|
||||
} else {
|
||||
return resolvePageComponent(
|
||||
`./Pages/${name}.vue`,
|
||||
import.meta.glob<DefineComponent>('./Pages/**/*.vue')
|
||||
);
|
||||
return resolvePageComponent(pagePath, extensionPages);
|
||||
}
|
||||
|
||||
return resolvePageComponent(
|
||||
`./Pages/${name}.vue`,
|
||||
import.meta.glob<DefineComponent>('./Pages/**/*.vue')
|
||||
);
|
||||
},
|
||||
setup({ el, App, props, plugin }) {
|
||||
const app = createApp({ render: () => h(App, props) });
|
||||
|
||||
Reference in New Issue
Block a user