Skip to content
Commits on Source (4)
......@@ -39,7 +39,7 @@ stages:
- !reference [.rules-map, on-dev]
- !reference [.rules-map, not-on-semantic-release-commit]
- !reference [.rules-map, on-branch]
image: hub.eole.education/proxyhub/geoffreybooth/meteor-base:2.12
image: hub.eole.education/proxyhub/geoffreybooth/meteor-base:2.15
cache:
key:
files:
......
......@@ -5,15 +5,15 @@
# but you can also edit it by hand.
meteor-base@1.5.1 # Packages every Meteor app needs to have
mobile-experience@1.1.0 # Packages for a great mobile UX
mongo@1.16.6 # The database Meteor supports right now
mobile-experience@1.1.1 # Packages for a great mobile UX
mongo@1.16.8 # The database Meteor supports right now
reactive-var@1.0.12 # Reactive variable for tracker
standard-minifier-css@1.9.2 # CSS minifier run for production mode
standard-minifier-js@2.8.1 # JS minifier run for production mode
es5-shim@4.8.0 # ECMAScript 5 compatibility for older browsers
ecmascript@0.16.7 # Enable ECMAScript2015+ syntax in app code
typescript@4.9.4 # Enable TypeScript syntax in .ts and .tsx modules
ecmascript@0.16.8 # Enable ECMAScript2015+ syntax in app code
typescript@4.9.5 # Enable TypeScript syntax in .ts and .tsx modules
shell-server@0.5.0 # Server-side component of the `meteor shell` command
hot-module-replacement@0.5.3 # Update client in development without reloading the page
......@@ -25,7 +25,7 @@ aldeed:collection2
aldeed:schema-index
mdg:validated-method
universe:i18n
accounts-password@2.3.4
accounts-password@2.4.0
alanning:roles
percolate:migrations
matb33:collection-hooks
......
accounts-base@2.2.8
accounts-password@2.3.4
alanning:roles@3.4.0
accounts-base@2.2.10
accounts-password@2.4.0
alanning:roles@3.6.3
aldeed:collection2@3.5.0
aldeed:schema-index@3.0.0
aldeed:schema-index@3.1.0
allow-deny@1.1.1
autoupdate@1.8.0
babel-compiler@7.10.4
babel-compiler@7.10.5
babel-runtime@1.5.1
base64@1.0.12
binary-heap@1.0.11
blaze-tools@1.1.3
boilerplate-generator@1.7.1
blaze-tools@1.1.4
boilerplate-generator@1.7.2
caching-compiler@1.2.2
caching-html-compiler@1.2.1
caching-html-compiler@1.2.2
callback-hook@1.5.1
check@1.3.2
dburles:collection-helpers@1.1.0
ddp@1.4.1
ddp-client@2.6.1
ddp-common@1.4.0
ddp-rate-limiter@1.2.0
ddp-server@2.6.1
ddp-rate-limiter@1.2.1
ddp-server@2.7.0
diff-sequence@1.1.2
dynamic-import@0.7.3
ecmascript@0.16.7
ecmascript@0.16.8
ecmascript-runtime@0.8.1
ecmascript-runtime-client@0.12.1
ecmascript-runtime-server@0.11.0
ejson@1.1.3
email@2.2.5
es5-shim@4.8.0
fetch@0.1.3
fetch@0.1.4
geojson-utils@1.0.11
hot-code-push@1.0.4
hot-module-replacement@0.5.3
html-tools@1.1.3
htmljs@1.1.1
html-tools@1.1.4
htmljs@1.2.1
id-map@1.1.1
inter-process-messaging@0.1.1
launch-screen@1.3.0
launch-screen@2.0.0
localstorage@1.2.0
logging@1.3.2
matb33:collection-hooks@1.2.2
logging@1.3.3
matb33:collection-hooks@1.3.1
mdg:validated-method@1.3.0
meteor@1.11.2
meteor@1.11.5
meteor-base@1.5.1
meteorhacks:picker@1.0.3
minifier-css@1.6.4
minifier-js@2.7.5
minimongo@1.9.3
mobile-experience@1.1.0
mobile-experience@1.1.1
mobile-status-bar@1.1.0
modern-browsers@0.1.9
modules@0.19.0
modern-browsers@0.1.10
modules@0.20.0
modules-runtime@0.13.1
modules-runtime-hot@0.14.2
mongo@1.16.6
mongo@1.16.9
mongo-decimal@0.1.3
mongo-dev-server@1.1.0
mongo-id@1.0.8
npm-mongo@4.16.0
npm-mongo@4.17.2
ordered-dict@1.1.0
percolate:migrations@1.1.1
promise@0.12.2
raix:eventemitter@1.0.0
random@1.2.1
rate-limit@1.1.1
react-fast-refresh@0.2.7
react-fast-refresh@0.2.8
reactive-var@1.0.12
reload@1.3.1
retry@1.1.0
......@@ -74,18 +74,18 @@ routepolicy@1.1.1
seba:method-hooks@3.0.3
sha@1.0.9
shell-server@0.5.0
socket-stream-client@0.5.1
spacebars-compiler@1.3.1
socket-stream-client@0.5.2
spacebars-compiler@1.3.2
standard-minifier-css@1.9.2
standard-minifier-js@2.8.1
static-html@1.3.2
templating-tools@1.2.2
templating-tools@1.2.3
tmeasday:check-npm-versions@1.0.2
tracker@1.3.2
typescript@4.9.4
underscore@1.0.13
tracker@1.3.3
typescript@4.9.5
underscore@1.6.1
universe:i18n@1.32.6
url@1.3.2
webapp@1.13.5
webapp@1.13.8
webapp-hashing@1.1.1
zodern:types@1.0.9
zodern:types@1.0.13
# The tag here should match the Meteor version of your app, per .meteor/release
FROM hub.eole.education/proxyhub/geoffreybooth/meteor-base:2.12
FROM hub.eole.education/proxyhub/geoffreybooth/meteor-base:2.15
# Copy app package.json and package-lock.json into container
#COPY ./app/package*.json $APP_SOURCE_FOLDER/
......@@ -14,7 +14,7 @@ RUN bash $SCRIPTS_FOLDER/build-meteor-bundle.sh
# Rather than Node 8 latest (Alpine), you can also use the specific version of Node expected by your Meteor release, per https://docs.meteor.com/changelog.html
FROM hub.eole.education/proxyhub/library/node:14.21.3-alpine
FROM hub.eole.education/proxyhub/meteor/node:14.21.4-alpine3.17
ENV APP_BUNDLE_FOLDER /opt/bundle
ENV SCRIPTS_FOLDER /docker
......@@ -37,7 +37,7 @@ RUN bash $SCRIPTS_FOLDER/build-meteor-npm-dependencies.sh --build-from-source
# Start another Docker stage, so that the final image doesn’t contain the layer with the build dependencies
# See previous FROM line; this must match
FROM hub.eole.education/proxyhub/library/node:14.21.3-alpine
FROM hub.eole.education/proxyhub/meteor/node:14.21.4-alpine3.17
ENV APP_BUNDLE_FOLDER /opt/bundle
ENV SCRIPTS_FOLDER /docker
......
/* eslint-disable func-names */
import { Mongo } from 'meteor/mongo';
import SimpleSchema from 'simpl-schema';
import { Tracker } from 'meteor/tracker';
import { getLabel } from '../../utils/utils';
import { ArticleType } from '/imports/types/collections';
import RegEx from '/imports/utils/regExp';
const Articles: Mongo.Collection<ArticleType> = new Mongo.Collection('articles');
......@@ -43,7 +43,7 @@ Articles.schema = new SimpleSchema(
optional: true,
},
structure: {
type: SimpleSchema.RegEx.Id,
type: RegEx.Id,
label: getLabel('api.articles.labels.structure'),
},
markdown: {
......@@ -60,18 +60,10 @@ Articles.schema = new SimpleSchema(
defaultValue: [],
label: getLabel('api.articles.labels.groups'),
},
'groups.$': {
type: { type: Object },
},
'groups.$._id': {
type: { type: String, regEx: SimpleSchema.RegEx.Id },
},
'groups.$.name': {
type: { type: String },
},
'groups.$.type': {
type: { type: SimpleSchema.Integer },
},
'groups.$': { type: Object },
'groups.$._id': { type: String, regEx: RegEx.Id },
'groups.$.name': { type: String },
'groups.$.type': { type: SimpleSchema.Integer },
createdAt: {
type: Date,
label: getLabel('api.articles.labels.createdAt'),
......@@ -90,7 +82,7 @@ Articles.schema = new SimpleSchema(
defaultValue: '',
},
},
{ clean: { removeEmptyStrings: false }, tracker: Tracker },
{ clean: { removeEmptyStrings: false } },
);
Articles.publicFields = {
......
import { Mongo } from 'meteor/mongo';
import SimpleSchema from 'simpl-schema';
import { Tracker } from 'meteor/tracker';
import { getLabel } from '../../utils/utils';
import { BookmarksType } from '/imports/types/collections';
......@@ -50,7 +49,7 @@ Bookmarks.schema = new SimpleSchema(
defaultValue: '',
},
},
{ clean: { removeEmptyStrings: false }, tracker: Tracker },
{ clean: { removeEmptyStrings: false } },
);
Bookmarks.publicFields = {
......
import { Mongo } from 'meteor/mongo';
import SimpleSchema from 'simpl-schema';
import { Tracker } from 'meteor/tracker';
import { getLabel } from '../../utils/utils';
import { EventType } from '/imports/types/collections';
import RegEx from '/imports/utils/regExp';
const Events: Mongo.Collection<EventType> = new Mongo.Collection('events');
......@@ -47,99 +47,96 @@ const settingsParticipant = new SimpleSchema({
},
groupId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
regEx: RegEx.Id,
optional: true,
},
});
Events.schema = new SimpleSchema(
{
_id: {
type: String,
regEx: SimpleSchema.RegEx.Id,
min: 1,
},
title: {
type: String,
},
location: {
type: String,
optional: true,
},
recurrent: {
type: Boolean,
defaultValue: false,
},
daysOfWeek: {
type: Array,
optional: true,
},
'daysOfWeek.$': {
type: Number,
optional: true,
},
description: {
type: String,
optional: true,
},
start: {
type: Date,
},
end: {
type: Date,
},
startTime: {
type: String,
optional: true,
},
endTime: {
type: String,
optional: true,
},
allDay: {
type: Boolean,
defaultValue: false,
},
groups: {
type: Array,
defaultValue: [],
},
'groups.$': {
type: settingsGroup,
optional: true,
},
participants: {
type: Array,
defaultValue: [],
},
'participants.$': {
type: settingsParticipant,
optional: true,
},
guests: {
type: Array,
defaultValue: [],
},
'guests.$': {
type: String,
optional: true,
},
userId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
createdAt: {
type: Date,
label: getLabel('api.articles.labels.createdAt'),
optional: true,
},
updatedAt: {
type: Date,
label: getLabel('api.articles.labels.updatedAt'),
},
},
{ tracker: Tracker },
);
Events.schema = new SimpleSchema({
_id: {
type: String,
regEx: RegEx.Id,
min: 1,
},
title: {
type: String,
},
location: {
type: String,
optional: true,
},
recurrent: {
type: Boolean,
defaultValue: false,
},
daysOfWeek: {
type: Array,
optional: true,
},
'daysOfWeek.$': {
type: Number,
optional: true,
},
description: {
type: String,
optional: true,
},
start: {
type: Date,
},
end: {
type: Date,
},
startTime: {
type: String,
optional: true,
},
endTime: {
type: String,
optional: true,
},
allDay: {
type: Boolean,
defaultValue: false,
},
groups: {
type: Array,
defaultValue: [],
},
'groups.$': {
type: settingsGroup,
optional: true,
},
participants: {
type: Array,
defaultValue: [],
},
'participants.$': {
type: settingsParticipant,
optional: true,
},
guests: {
type: Array,
defaultValue: [],
},
'guests.$': {
type: String,
optional: true,
},
userId: {
type: String,
regEx: RegEx.Id,
},
createdAt: {
type: Date,
label: getLabel('api.articles.labels.createdAt'),
optional: true,
},
updatedAt: {
type: Date,
label: getLabel('api.articles.labels.updatedAt'),
},
});
Events.attachSchema(Events.schema);
......
import { Mongo } from 'meteor/mongo';
import SimpleSchema from 'simpl-schema';
import { Tracker } from 'meteor/tracker';
import slugify from 'slugify';
import { getLabel } from '../../utils/utils';
import { GroupType } from '/imports/types/collections';
import RegEx from '/imports/utils/regExp';
const Groups: Mongo.Collection<GroupType> = new Mongo.Collection('groups');
......@@ -20,131 +20,128 @@ Groups.deny({
},
});
Groups.schema = new SimpleSchema(
{
name: {
type: String,
// index: true,
// unique: true,
min: 1,
max: 60,
label: getLabel('api.groups.labels.name'),
},
slug: {
type: String,
// index: true,
// unique: true,
min: 1,
label: getLabel('api.groups.labels.slug'),
autoValue() {
const name = this.field('name').value;
// if name is not being modified, do not calculate autovalue
if (name === undefined) return undefined;
const slug = slugify(name, {
replacement: '-', // replace spaces with replacement
lower: true, // result in lower case
});
return slug;
},
},
description: {
type: String,
optional: true,
label: getLabel('api.groups.labels.description'),
},
content: {
type: String,
optional: true,
label: getLabel('api.groups.labels.content'),
},
active: { type: Boolean, label: getLabel('api.groups.labels.active') },
groupPadID: {
type: String,
optional: true,
label: getLabel('api.groups.labels.groupPadID'),
},
digest: {
type: String,
optional: true,
label: getLabel('api.groups.labels.digest'),
},
type: {
type: SimpleSchema.Integer,
allowedValues: [0, 5, 10], // 0 Ouvert, 5 Modéré, 10 Fermé
label: getLabel('api.groups.labels.type'),
},
avatar: {
type: String,
optional: true,
label: getLabel('api.users.labels.avatar'),
defaultValue: '',
},
applications: {
type: Array,
optional: true,
},
'applications.$': {
type: String,
label: getLabel('api.groups.labels.applications'),
},
owner: {
type: String,
regEx: SimpleSchema.RegEx.Id,
label: getLabel('api.groups.labels.owner'),
},
admins: {
type: Array,
defaultValue: [],
label: getLabel('api.groups.labels.admins'),
},
'admins.$': { type: String, regEx: SimpleSchema.RegEx.Id },
animators: {
type: Array,
defaultValue: [],
label: getLabel('api.groups.labels.animators'),
},
'animators.$': { type: String, regEx: SimpleSchema.RegEx.Id },
members: {
type: Array,
defaultValue: [],
label: getLabel('api.groups.labels.members'),
},
'members.$': { type: String, regEx: SimpleSchema.RegEx.Id },
candidates: {
type: Array,
defaultValue: [],
label: getLabel('api.groups.labels.candidates'),
},
'candidates.$': { type: String, regEx: SimpleSchema.RegEx.Id },
numCandidates: {
type: Number,
defaultValue: 0,
label: getLabel('api.groups.labels.numCandidates'),
},
plugins: {
type: Object,
defaultValue: {},
optional: true,
blackbox: true,
label: getLabel('api.groups.labels.plugins'),
},
articles: {
type: Boolean,
defaultValue: false,
label: getLabel('api.groups.labels.articles'),
},
meeting: {
// server-side only, do not publish
type: Object,
defaultValue: {},
label: getLabel('api.groups.labels.meetingParams'),
Groups.schema = new SimpleSchema({
name: {
type: String,
// index: true,
// unique: true,
min: 1,
max: 60,
label: getLabel('api.groups.labels.name'),
},
slug: {
type: String,
// index: true,
// unique: true,
min: 1,
label: getLabel('api.groups.labels.slug'),
autoValue() {
const name = this.field('name').value;
// if name is not being modified, do not calculate autovalue
if (name === undefined) return undefined;
const slug = slugify(String(name), {
replacement: '-', // replace spaces with replacement
lower: true, // result in lower case
});
return slug;
},
'meeting.attendeePW': { type: String, defaultValue: '' },
'meeting.moderatorPW': { type: String, defaultValue: '' },
'meeting.createTime': { type: String, defaultValue: '' },
},
{ tracker: Tracker },
);
description: {
type: String,
optional: true,
label: getLabel('api.groups.labels.description'),
},
content: {
type: String,
optional: true,
label: getLabel('api.groups.labels.content'),
},
active: { type: Boolean, label: getLabel('api.groups.labels.active') },
groupPadID: {
type: String,
optional: true,
label: getLabel('api.groups.labels.groupPadID'),
},
digest: {
type: String,
optional: true,
label: getLabel('api.groups.labels.digest'),
},
type: {
type: SimpleSchema.Integer,
allowedValues: [0, 5, 10], // 0 Ouvert, 5 Modéré, 10 Fermé
label: getLabel('api.groups.labels.type'),
},
avatar: {
type: String,
optional: true,
label: getLabel('api.users.labels.avatar'),
defaultValue: '',
},
applications: {
type: Array,
optional: true,
},
'applications.$': {
type: String,
label: getLabel('api.groups.labels.applications'),
},
owner: {
type: String,
regEx: RegEx.Id,
label: getLabel('api.groups.labels.owner'),
},
admins: {
type: Array,
defaultValue: [],
label: getLabel('api.groups.labels.admins'),
},
'admins.$': { type: String, regEx: RegEx.Id },
animators: {
type: Array,
defaultValue: [],
label: getLabel('api.groups.labels.animators'),
},
'animators.$': { type: String, regEx: RegEx.Id },
members: {
type: Array,
defaultValue: [],
label: getLabel('api.groups.labels.members'),
},
'members.$': { type: String, regEx: RegEx.Id },
candidates: {
type: Array,
defaultValue: [],
label: getLabel('api.groups.labels.candidates'),
},
'candidates.$': { type: String, regEx: RegEx.Id },
numCandidates: {
type: Number,
defaultValue: 0,
label: getLabel('api.groups.labels.numCandidates'),
},
plugins: {
type: Object,
defaultValue: {},
optional: true,
blackbox: true,
label: getLabel('api.groups.labels.plugins'),
},
articles: {
type: Boolean,
defaultValue: false,
label: getLabel('api.groups.labels.articles'),
},
meeting: {
// server-side only, do not publish
type: Object,
defaultValue: {},
label: getLabel('api.groups.labels.meetingParams'),
},
'meeting.attendeePW': { type: String, defaultValue: '' },
'meeting.moderatorPW': { type: String, defaultValue: '' },
'meeting.createTime': { type: String, defaultValue: '' },
});
// Groups.after.update(function countCandidates(_, doc, fieldNames) {
// if (fieldNames.includes('candidates')) {
......
......@@ -7,6 +7,7 @@ import i18n from 'meteor/universe:i18n';
import { isActive, getLabel } from '../../utils/utils';
import Notifications, { notificationSchema } from './notifications';
import { NotificationType } from '/imports/types/collections';
import RegEx from '/imports/utils/regExp';
export function addExpiration(data: Partial<NotificationType>) {
const finalData = { ...data };
......@@ -61,7 +62,7 @@ export const removeNotification = new ValidatedMethod({
validate: new SimpleSchema({
notificationId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
regEx: RegEx.Id,
label: getLabel('api.notifications.labels.id'),
},
}).validator(),
......@@ -120,7 +121,7 @@ export const markNotificationAsRead = new ValidatedMethod({
validate: new SimpleSchema({
notificationId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
regEx: RegEx.Id,
label: getLabel('api.notifications.labels.id'),
},
}).validator(),
......
import { Mongo } from 'meteor/mongo';
import { Meteor } from 'meteor/meteor';
import SimpleSchema from 'simpl-schema';
import { Tracker } from 'meteor/tracker';
import { getLabel } from '/imports/utils/utils';
import { NotificationType } from '/imports/types/collections';
......@@ -20,38 +19,35 @@ Notifications.deny({
},
});
export const notificationSchema = new SimpleSchema(
{
userId: {
type: String,
label: getLabel('api.notifications.labels.userId'),
},
title: { type: String, optional: true, label: getLabel('api.notifications.labels.title') },
content: { type: String, optional: true, label: getLabel('api.notifications.labels.content') },
type: {
type: String,
label: getLabel('api.notifications.labels.type'),
},
link: { type: String, optional: true, label: getLabel('api.notifications.labels.link') },
createdAt: {
type: Date,
label: getLabel('api.notifications.labels.createdAt'),
autoValue() {
if (!this.isSet) {
return new Date();
}
return this.value;
},
},
expireAt: {
type: Date,
label: getLabel('api.notifications.labels.createdAt'),
optional: true,
export const notificationSchema = new SimpleSchema({
userId: {
type: String,
label: getLabel('api.notifications.labels.userId'),
},
title: { type: String, optional: true, label: getLabel('api.notifications.labels.title') },
content: { type: String, optional: true, label: getLabel('api.notifications.labels.content') },
type: {
type: String,
label: getLabel('api.notifications.labels.type'),
},
link: { type: String, optional: true, label: getLabel('api.notifications.labels.link') },
createdAt: {
type: Date,
label: getLabel('api.notifications.labels.createdAt'),
autoValue() {
if (!this.isSet) {
return new Date();
}
return this.value;
},
read: { type: Boolean, defaultValue: false, label: getLabel('api.notifications.labels.read') },
},
{ tracker: Tracker },
);
expireAt: {
type: Date,
label: getLabel('api.notifications.labels.createdAt'),
optional: true,
},
read: { type: Boolean, defaultValue: false, label: getLabel('api.notifications.labels.read') },
});
Notifications.publicFields = {
userId: 1,
......
import { Mongo } from 'meteor/mongo';
import SimpleSchema from 'simpl-schema';
import { Tracker } from 'meteor/tracker';
import { PollType } from '/imports/types/collections';
import RegEx from '/imports/utils/regExp';
const Polls: Mongo.Collection<PollType> = new Mongo.Collection('polls');
......@@ -53,7 +53,7 @@ Polls.schema = new SimpleSchema(
{
_id: {
type: String,
regEx: SimpleSchema.RegEx.Id,
regEx: RegEx.Id,
optional: true,
},
title: {
......@@ -63,7 +63,7 @@ Polls.schema = new SimpleSchema(
},
userId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
regEx: RegEx.Id,
label: 'Owner',
},
description: {
......@@ -114,7 +114,7 @@ Polls.schema = new SimpleSchema(
},
'groups.$': {
type: String,
regEx: SimpleSchema.RegEx.Id,
regEx: RegEx.Id,
},
dates: {
type: Array,
......@@ -141,7 +141,7 @@ Polls.schema = new SimpleSchema(
label: 'Updated date',
},
},
{ clean: { removeEmptyStrings: false }, tracker: Tracker },
{ clean: { removeEmptyStrings: false } },
);
Polls.publicFields = {
......
import { Mongo } from 'meteor/mongo';
import SimpleSchema from 'simpl-schema';
import { Tracker } from 'meteor/tracker';
import { getLabel } from '/imports/utils/utils';
import { ServiceType } from '/imports/types/collections';
import RegEx from '/imports/utils/regExp';
const Services = new Mongo.Collection<ServiceType>('services');
......@@ -54,7 +54,7 @@ Services.schema = new SimpleSchema(
defaultValue: [],
label: getLabel('api.services.labels.categories'),
},
'categories.$': { type: String, regEx: SimpleSchema.RegEx.Id },
'categories.$': { type: String, regEx: RegEx.Id },
screenshots: {
type: Array,
defaultValue: [],
......@@ -62,7 +62,7 @@ Services.schema = new SimpleSchema(
},
'screenshots.$': { type: String },
structure: {
type: SimpleSchema.RegEx.Id,
type: RegEx.Id,
label: getLabel('api.services.labels.structure'),
defaultValue: '',
},
......@@ -72,7 +72,7 @@ Services.schema = new SimpleSchema(
label: getLabel('api.services.labels.offline'),
},
},
{ clean: { removeEmptyStrings: false }, tracker: Tracker },
{ clean: { removeEmptyStrings: false } },
);
Services.publicFields = {
......
import { Mongo } from 'meteor/mongo';
import SimpleSchema from 'simpl-schema';
import PropTypes from 'prop-types';
import { Tracker } from 'meteor/tracker';
import { StructureType } from '/imports/types/collections';
import { getLabel } from '/imports/utils/utils';
import RegEx from '/imports/utils/regExp';
const Structures: Mongo.Collection<StructureType> = new Mongo.Collection('structures');
......@@ -54,98 +54,93 @@ export const defaultIntroduction = [
},
];
Structures.schema = new SimpleSchema(
{
name: {
type: String,
min: 1,
label: getLabel('api.structures.labels.name'),
},
parentId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
label: getLabel('api.structures.labels.parentId'),
optional: true,
defaultValue: null,
},
childrenIds: {
type: Array,
label: getLabel('api.structures.labels.childrenIds'),
defaultValue: [],
},
'childrenIds.$': { type: String, regEx: SimpleSchema.RegEx.Id },
ancestorsIds: {
type: Array,
label: getLabel('api.structures.labels.ancestorsIds'),
defaultValue: [],
},
'ancestorsIds.$': { type: String, regEx: SimpleSchema.RegEx.Id },
introduction: {
type: Array,
label: getLabel('api.structures.labels.introduction.label'),
defaultValue: defaultIntroduction,
},
'introduction.$': {
type: IntroductionSchema,
},
contactEmail: {
type: String,
label: getLabel('api.structures.labels.contactEmail'),
optional: true,
defaultValue: null,
regEx: SimpleSchema.RegEx.Email,
},
groupId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
label: getLabel('api.structures.labels.groupId'),
optional: true,
defaultValue: null,
},
asamExtensionsIds: {
type: Array,
defaultValue: [],
label: getLabel('api.structures.labels.asamExtensionsIds'),
},
'asamExtensionsIds.$': { type: String, regEx: SimpleSchema.RegEx.Id },
userStructureValidationMandatory: {
type: Boolean,
optional: true,
label: getLabel('api.appsettings.label.userStructureValidationMandatory'),
},
iconUrlImage: {
type: String,
label: getLabel('api.structures.labels.iconUrlImage'),
optional: true,
},
coverUrlImage: {
type: String,
label: getLabel('api.structures.labels.coverUrlImage'),
optional: true,
},
externalUrl: {
type: String,
label: getLabel('api.structures.labels.externalUrl'),
optional: true,
defaultValue: null,
},
sendMailToParent: {
type: Boolean,
optional: true,
defaultValue: false,
label: getLabel('api.appsettings.label.sendMailToParent'),
},
sendMailToStructureAdmin: {
type: Boolean,
optional: true,
defaultValue: false,
label: getLabel('api.appsettings.label.sendMailToStructureAdmin'),
},
Structures.schema = new SimpleSchema({
name: {
type: String,
min: 1,
label: getLabel('api.structures.labels.name'),
},
{
tracker: Tracker,
parentId: {
type: String,
regEx: RegEx.Id,
label: getLabel('api.structures.labels.parentId'),
optional: true,
defaultValue: null,
},
childrenIds: {
type: Array,
label: getLabel('api.structures.labels.childrenIds'),
defaultValue: [],
},
'childrenIds.$': { type: String, regEx: RegEx.Id },
ancestorsIds: {
type: Array,
label: getLabel('api.structures.labels.ancestorsIds'),
defaultValue: [],
},
'ancestorsIds.$': { type: String, regEx: RegEx.Id },
introduction: {
type: Array,
label: getLabel('api.structures.labels.introduction.label'),
defaultValue: defaultIntroduction,
},
'introduction.$': {
type: IntroductionSchema,
},
contactEmail: {
type: String,
label: getLabel('api.structures.labels.contactEmail'),
optional: true,
defaultValue: null,
regEx: RegEx.Email,
},
groupId: {
type: String,
regEx: RegEx.Id,
label: getLabel('api.structures.labels.groupId'),
optional: true,
defaultValue: null,
},
);
asamExtensionsIds: {
type: Array,
defaultValue: [],
label: getLabel('api.structures.labels.asamExtensionsIds'),
},
'asamExtensionsIds.$': { type: String, regEx: RegEx.Id },
userStructureValidationMandatory: {
type: Boolean,
optional: true,
label: getLabel('api.appsettings.label.userStructureValidationMandatory'),
},
iconUrlImage: {
type: String,
label: getLabel('api.structures.labels.iconUrlImage'),
optional: true,
},
coverUrlImage: {
type: String,
label: getLabel('api.structures.labels.coverUrlImage'),
optional: true,
},
externalUrl: {
type: String,
label: getLabel('api.structures.labels.externalUrl'),
optional: true,
defaultValue: null,
},
sendMailToParent: {
type: Boolean,
optional: true,
defaultValue: false,
label: getLabel('api.appsettings.label.sendMailToParent'),
},
sendMailToStructureAdmin: {
type: Boolean,
optional: true,
defaultValue: false,
label: getLabel('api.appsettings.label.sendMailToStructureAdmin'),
},
});
Structures.publicFields = {
_id: 1,
......
import { Meteor } from 'meteor/meteor';
import SimpleSchema from 'simpl-schema';
import { Tracker } from 'meteor/tracker';
import { Roles } from 'meteor/alanning:roles';
import { getLabel } from '../../utils/utils';
import RegEx from '/imports/utils/regExp';
// import { getRandomNCloudURL } from '../nextcloud/methods';
const AppRoles = ['candidate', 'member', 'animator', 'admin', 'adminStructure'];
......@@ -34,7 +34,7 @@ Meteor.users.schema = new SimpleSchema(
},
'emails.$.address': {
type: String,
regEx: SimpleSchema.RegEx.Email,
regEx: RegEx.Email,
label: getLabel('api.users.labels.emailAddress'),
},
'emails.$.verified': {
......@@ -86,25 +86,19 @@ Meteor.users.schema = new SimpleSchema(
defaultValue: [],
label: getLabel('api.users.labels.favServices'),
},
'favServices.$': {
type: { type: String, regEx: SimpleSchema.RegEx.Id },
},
'favServices.$': { type: String, regEx: RegEx.Id },
favGroups: {
type: Array,
defaultValue: [],
label: getLabel('api.users.labels.favGroups'),
},
'favGroups.$': {
type: { type: String, regEx: SimpleSchema.RegEx.Id },
},
'favGroups.$': { type: String, regEx: RegEx.Id },
favUserBookmarks: {
type: Array,
defaultValue: [],
label: getLabel('api.users.labels.favUserBookmarks'),
},
'favUserBookmarks.$': {
type: { type: String, regEx: SimpleSchema.RegEx.Id },
},
'favUserBookmarks.$': { type: String, regEx: RegEx.Id },
structure: {
type: String,
optional: true,
......@@ -112,7 +106,7 @@ Meteor.users.schema = new SimpleSchema(
},
primaryEmail: {
type: String,
regEx: SimpleSchema.RegEx.Email,
regEx: RegEx.Email,
optional: true,
label: getLabel('api.users.labels.primaryEmail'),
},
......@@ -183,7 +177,7 @@ Meteor.users.schema = new SimpleSchema(
label: getLabel('api.users.labels.authToken'),
},
},
{ clean: { removeEmptyStrings: false }, tracker: Tracker },
{ clean: { removeEmptyStrings: false } },
);
Meteor.users.helpers({
......
// this domain regex matches all domains that have at least one .
// sadly IPv4 Adresses will be caught too but technically those are valid domains
// this expression is extracted from the original RFC 5322 mail expression
// a modification enforces that the tld consists only of characters
// this domain regex matches everythign that could be a domain in intranet
const rxDomain = '(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z](?:[a-z-]*[a-z])?';
// that means "localhost" is a valid domain
// strict IPv4 expression which allows 0-255 per oktett
const rxNameDomain = '(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\.|$))+';
// strict IPv6 expression which allows (and validates) all shortcuts
const rxIPv4 = '(?:(?:[0-1]?\\d{1,2}|2[0-4]\\d|25[0-5])(?:\\.|$)){4}';
const rxIPv6 =
'(?:(?:[\\dA-Fa-f]{1,4}(?::|$)){8}' + // full adress
'|(?=(?:[^:\\s]|:[^:\\s])*::(?:[^:\\s]|:[^:\\s])*$)' + // or min/max one '::'
'[\\dA-Fa-f]{0,4}(?:::?(?:[\\dA-Fa-f]{1,4}|$)){1,6})'; // and short adress
// this allows domains (also localhost etc) and ip adresses
const rxWeakDomain = '(?:'.concat([rxNameDomain, rxIPv4, rxIPv6].join('|'), ')');
// min and max are used to set length boundaries
// set both for explicit lower and upper bounds
// set min as integer and max to null for explicit lower bound and arbitrary upper bound
// set none for arbitrary length
// set only min for fixed length
// character list: https://github.com/meteor/meteor/blob/release/0.8.0/packages/random/random.js#L88
// string length: https://github.com/meteor/meteor/blob/release/0.8.0/packages/random/random.js#L143
const isValidBound = function isValidBound(value: any, lower: any) {
return !value || (Number.isSafeInteger(value) && value > lower);
};
// unique id from the random package also used by minimongo
const idOfLength = function idOfLength(min: any, max: any) {
if (!isValidBound(min, 0))
throw new Error('Expected a non-negative safe integer, got '.concat(min));
if (!isValidBound(max, min))
throw new Error(
'Expected a non-negative safe integer greater than 1 and greater than min, got '.concat(max),
);
let bounds;
if (min && max) bounds = ''.concat(min, ',').concat(max);
else if (min && max === null) bounds = ''.concat(min, ',');
else if (min && !max) bounds = ''.concat(min);
else if (!min && !max) bounds = '0,';
else throw new Error('Unexpected state for min ('.concat(min, ') and max (').concat(max, ')'));
return new RegExp(
'^[23456789ABCDEFGHJKLMNPQRSTWXYZabcdefghijkmnopqrstuvwxyz]{'.concat(bounds, '}$'),
);
};
const RegEx = {
// We use the RegExp suggested by W3C in http://www.w3.org/TR/html5/forms.html#valid-e-mail-address
// This is probably the same logic used by most browsers when type=email, which is our goal. It is
// a very permissive expression. Some apps may wish to be more strict and can write their own RegExp.
Email:
// eslint-disable-next-line max-len
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,
// Like Email but requires the TLD (.com, etc)
EmailWithTLD:
// eslint-disable-next-line max-len
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
Domain: new RegExp('^'.concat(rxDomain, '$')),
WeakDomain: new RegExp('^'.concat(rxWeakDomain, '$')),
IP: new RegExp('^(?:'.concat(rxIPv4, '|').concat(rxIPv6, ')$')),
IPv4: new RegExp('^'.concat(rxIPv4, '$')),
IPv6: new RegExp('^'.concat(rxIPv6, '$')),
// URL RegEx from https://gist.github.com/dperini/729294
// DEPRECATED! Known 2nd degree polynomial ReDoS vulnerability.
// Use a custom validator such as this to validate URLs:
// custom() {
// if (!this.isSet) return;
// try {
// new URL(this.value);
// } catch (err) {
// return 'badUrl';
// }
// }
// eslint-disable-next-line max-len
Url: /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$/i,
// default id is defined with exact 17 chars of length
Id: idOfLength(17, null),
idOfLength,
// allows for a 5 digit zip code followed by a whitespace or dash and then 4 more digits
// matches 11111 and 11111-1111 and 11111 1111
ZipCode: /^\d{5}(?:[-\s]\d{4})?$/,
// taken from Google's libphonenumber library
// https://github.com/googlei18n/libphonenumber/blob/master/javascript/i18n/phonenumbers/phonenumberutil.js
// reference the VALID_PHONE_NUMBER_PATTERN key
// allows for common phone number symbols including + () and -
// DEPRECATED! Known 2nd degree polynomial ReDoS vulnerability.
// Instead, use a custom validation function, with a high quality
// phone number validation package that meets your needs.
Phone:
// eslint-disable-next-line max-len
/^[0-90-9٠-٩۰-۹]{2}$|^[++]*(?:[-x‐-―−ー--/ ­​⁠ ()()[].[\]/~⁓∼~*]*[0-90-9٠-٩۰-۹]){3,}[-x‐-―−ー--/ ­​⁠ ()()[].[\]/~⁓∼~*A-Za-z0-90-9٠-٩۰-۹]*(?:;ext=([0-90-9٠-٩۰-۹]{1,20})|[ \t,]*(?:e?xt(?:ensi(?:?))?n?|e?xtn?|доб|anexo)[:..]?[ \t,-]*([0-90-9٠-٩۰-۹]{1,20})#?|[ \t,]*(?:[xx##~~]|int|int)[:..]?[ \t,-]*([0-90-9٠-٩۰-۹]{1,9})#?|[- ]+([0-90-9٠-٩۰-۹]{1,6})#|[ \t]*(?:,{2}|;)[:..]?[ \t,-]*([0-90-9٠-٩۰-۹]{1,15})#?|[ \t]*(?:,)+[:..]?[ \t,-]*([0-90-9٠-٩۰-۹]{1,9})#?)?$/i, // eslint-disable-line no-irregular-whitespace
};
export default RegEx;
......@@ -3,12 +3,6 @@ import i18n from 'meteor/universe:i18n';
import SimpleSchema from 'simpl-schema';
import { UserType } from '../types/collections';
type CtxType = {
label: string;
regExp: string;
dataType?: any;
};
export const isActive = (userId: string) => {
if (!userId) return false;
const user: Partial<UserType> | undefined = Meteor.users.findOne(userId, {
......@@ -32,66 +26,6 @@ export const checkPaginationParams = new SimpleSchema({
search: { type: String, defaultValue: '', label: getLabel('api.methods.labels.filter') },
});
export function registerSchemaMessages() {
const regExpMessages = [
{ exp: SimpleSchema.RegEx.Email, msg: 'SimpleSchema.RegEx.Email' },
{ exp: SimpleSchema.RegEx.EmailWithTLD, msg: 'SimpleSchema.RegEx.EmailWithTLD' },
{ exp: SimpleSchema.RegEx.Domain, msg: 'SimpleSchema.RegEx.Domain' },
{ exp: SimpleSchema.RegEx.WeakDomain, msg: 'SimpleSchema.RegEx.WeakDomain' },
{ exp: SimpleSchema.RegEx.IP, msg: 'SimpleSchema.RegEx.IP' },
{ exp: SimpleSchema.RegEx.IPv4, msg: 'SimpleSchema.RegEx.IPv4' },
{ exp: SimpleSchema.RegEx.IPv6, msg: 'SimpleSchema.RegEx.IPv6' },
{ exp: SimpleSchema.RegEx.Url, msg: 'SimpleSchema.RegEx.Url' },
{ exp: SimpleSchema.RegEx.Id, msg: 'SimpleSchema.RegEx.Id' },
{ exp: SimpleSchema.RegEx.ZipCode, msg: 'SimpleSchema.RegEx.ZipCode' },
{ exp: SimpleSchema.RegEx.Phone, msg: 'SimpleSchema.RegEx.Phone' },
];
SimpleSchema.setDefaultMessages({
messages: {
en: {
required: (ctx: CtxType) => i18n.__('SimpleSchema.required', ctx),
minString: (ctx: CtxType) => i18n.__('SimpleSchema.minString', ctx),
maxString: (ctx: CtxType) => i18n.__('SimpleSchema.maxString', ctx),
minNumber: (ctx: CtxType) => i18n.__('SimpleSchema.minNumber', ctx),
maxNumber: (ctx: CtxType) => i18n.__('SimpleSchema.maxNumber', ctx),
minNumberExclusive: (ctx: CtxType) => i18n.__('SimpleSchema.minNumberExclusive', ctx),
maxNumberExclusive: (ctx: CtxType) => i18n.__('SimpleSchema.maxNumberExclusive', ctx),
minDate: (ctx: CtxType) => i18n.__('SimpleSchema.minDate', ctx),
maxDate: (ctx: CtxType) => i18n.__('SimpleSchema.maxDate', ctx),
badDate: (ctx: CtxType) => i18n.__('SimpleSchema.badDate', ctx),
minCount: (ctx: CtxType) => i18n.__('SimpleSchema.minCount', ctx),
maxCount: (ctx: CtxType) => i18n.__('SimpleSchema.maxCount', ctx),
noDecimal: (ctx: CtxType) => i18n.__('SimpleSchema.noDecimal', ctx),
notAllowed: (ctx: CtxType) => i18n.__('SimpleSchema.notAllowed', ctx),
expectedType: (ctx: CtxType) => {
const finalCtx = { ...ctx };
const i18nEntry = `SimpleSchema.dataTypes.${ctx.dataType}`;
const typeTranslated = i18n.__(i18nEntry);
if (typeTranslated !== i18nEntry) {
// translatation for type is available
finalCtx.dataType = typeTranslated;
}
return i18n.__('SimpleSchema.expectedType', finalCtx);
},
keyNotInSchema: (ctx: CtxType) => i18n.__('SimpleSchema.keyNotInSchema', ctx),
regEx(ctx: CtxType) {
// See if there's one where exp matches this expression
let msgObj;
if (ctx.regExp) {
msgObj = regExpMessages.find((o) => o.exp && o.exp.toString() === ctx.regExp);
}
const regExpMessage = msgObj
? i18n.__(msgObj.msg)
: i18n.__('SimpleSchema.RegEx.Default');
return `${ctx.label} ${regExpMessage}`;
},
},
},
});
}
export function genRandomPassword(pwdlen = 16) {
// original code and explanations here :
// https://www.geeksforgeeks.org/how-to-generate-a-random-password-using-javascript/
......
This diff is collapsed.
......@@ -11,6 +11,7 @@
},
"dependencies": {
"@babel/runtime": "^7.22.15",
"@swagger-api/apidom-reference": "^0.99.2",
"@types/simpl-schema": "^1.12.0",
"@types/swagger-ui": "^3.52.0",
"@types/swagger-ui-react": "^4.1.0",
......@@ -23,14 +24,15 @@
"prettier": "^2.5.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"simpl-schema": "^1.12.0",
"simpl-schema": "^3.4.6",
"slugify": "^1.6.3",
"swagger-ui": "^4.1.3",
"swagger-ui-react": "^4.1.3"
},
"overrides": {
"axios": "^1.3.4",
"semver": "^7.5.2"
"semver": "^7.5.2",
"swagger-client": "3.19.8"
},
"devDependencies": {
"@types/cors": "^2.8.14",
......@@ -49,5 +51,5 @@
},
"testModule": "tests/main.ts"
},
"version": "1.0.1"
"version": "1.0.2-testing.1"
}